• 20181228 并发编程初步


     

     

    并发编程

    并发即同时发生,并发编程指程序中的多个任务同时被处理,其基于多道技术。

     

    并发与并行

    并发指的是多个事件同时发生,也称为伪并行(并发事实上是交替进行,如果切换速度足够快,那么就可以让人觉得是在同时发生)

    并行指的是多个事件同时进行

     

    阻塞与非阻塞

    阻塞:程序遇到I/O操作或是sleep,导致后续代码不能被CPU执行的状态。

    非阻塞:代码正常被CPU执行。

     

    多道技术:

    空间复用:内存分成几个部分,不同部分运行不同程序,彼此之间独立。 时间复用:当一个程序执行非计算操作(如I/O,等待用户输入;或操作系统强制切换),将CPU切到另一个程序进行计算操作。

    进程的三种状态:就绪态,运行态,阻塞态

    多道技术会在进程执行时间过长或遇到I/O时自动切换到其他进程,原进程被剥夺CPU执行权,需要重新进入就绪态后才能等待执行。

     

     

    进程:正在运行的程序就是进程,是一种抽象的概念。 进程依赖于操作系统,一个进程代表着一份资源(内存等),进程间内存相互独立,一个程序可以有多个进程。

    # 子进程中的数据修改,不会影响父进程,即子进程和父进程之间相互独立(子进程在运行前会拷贝父进程的环境,所以在子进程运行前要初始化好父进程的环境)
    import time
    a = 100
    def task():
       global a
       a = 0  # 子进程中的全局变量a已经变为0,但是父进程中的全局变量a,子进程无权修改。
       print("子进程",a)

    if __name__ == '__main__':
       p = Process(target=task)
       p.start()
       time.sleep(1)  # 注意此处的时间停顿,此处引出join函数(子进程全部结束再执行父进程),详见最后。
       print("主程序",a)
    输出结果为:
    子进程 0
    主程序 100

    与上面的做一下对比:

    两次结果的差异在于:子进程需要载入才能运行,这时候CPU可以进行主程序的其他内容操作。如果主程序睡眠,那么睡眠时间就够子程序执行完了。所以在输出顺序上存在差异。

    import time
    a = 100
    def task():
       global a
       a = 0
       print("子进程",a)

    if __name__ == '__main__':
       p = Process(target=task)
       p.start()
       print("主程序",a)
    输出结果为:
    主程序 100
    子进程 0

     

     

    PID与PPID

    PID:操作系统给每个进程编号,这个编号就是PID

    PPID:当进程A开启了另一个进程B,A称为B的父进程,B称为A的子进程,操作系统可以看做是所有主进程的父进程。

    import os
    while True:
       print("haha")
       print("self",os.getpid())  # 获取self的进程ID,ID号由操作系统分配
       print("parent",os.getppid())  # 第一个p指的是parent,即父进程的ID
       break
    # 每次的ID号都会不同,pid是当前执行程序,如果这段代码放到pycharm中运行,ppid就是pycharm的编号,如果这段代码放到cmd中运行,ppid就是cmd的编号。

     

    孤儿进程和僵尸进程:
    孤儿进程:父进程已经终结,但子进程仍在运行,无害,操作系统会负责回收处理等操作。
    僵尸进程:子进程执行完毕,残留一些信息(如进程id等)。但父进程没有处理残留信息,
    导致残留信息占用内存,有害。

     

    了解

    操作系统的两个核心作用: 1、屏蔽了复杂繁琐的硬件接口,为程序提供了简单易用的系统接口 2、将应用程序对硬件资源的竞争变成有序的使用。

    操作系统与普通应用程序的区别: 1、操作系统不能轻易被用户修改 2、操作系统源代码量巨大,仅内核通常就在五百万行以上 3、长寿,一旦完成不会轻易重写。

     

     

    python中开启子进程的两种方式

    方式1:

    实例化Process类

    from multiprocessing import Process
    import os

    def task():
       print("self",os.getpid())  # 自己的id
       print("parent",os.getppid())  # 父进程的id,此处的父进程是下面的p的那个进程(由p启动)
       print("task run")
    # win下创建子进程时,子进程会将父进程的代码加载一遍,导致重复创建子进程
    # 所以一定要将创建子进程的代码放到main的下面。
    if __name__ == '__main__':
       print("self",os.getpid())  # 当前执行内容的id
       print("parent",os.getppid())  # 通过pycharm执行,所以此处的父进程id是pycharm的id
       p = Process(target=task, name="子进程")  # 主进程启动子进程,目标子进程为task(注意此处仅为函数名)。创建一个表示进程的对象,并不是真正的创建进程,Process是一个类,他还有很多参数和方法,具体可以ctrl点进去查看。
       p.start()  # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态(但你爹永远是你爹(父进程),接产大夫(操作系统)不会成为隔壁老王)

    方式2:

    继承Process类 并覆盖run方法,子进程启动后会自动执行run方法。

    这种方法可以自定义进程的属性和行为,拓展对象的功能。

    class MyProcess(Process):  # 继承Process类
       def __init__(self,url):
           super().__init__()
           self.url = url
           
       def run(self):   # 覆盖run方法
           print("正在下载文件。。。",self.url)
           print("runrunrun")

    if __name__ == '__main__':
       p = MyProcess("www.baidu.com")
       p.start()  # 实例化了Process对象,start自动调用run。

    需要注意的是 在windows下 开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归

     

    join函数

    调用start函数后的操作就由操作系统来玩了,至于何时开启进程,进程何时执行,何时结束都与应用程序无关,所以当前进程会继续往下执行,join函数可以让父进程等待子进程结束后再执行。(即提高子进程优先级)

    案例1:

    from multiprocessing import Process
    import time

    x=1000

    def task():
       time.sleep(3)
       global x
       x=0
       print('儿子死啦',x)
    if __name__ == '__main__':
       p=Process(target=task)
       p.start()
       p.join() # 让父亲在原地等,子进程执行完毕才会执行下面的代码。
       print(x)

    案例2:

    from multiprocessing import Process
    import time
    def task(num):
       print("我是%s进程" %num)
       time.sleep(2) # 排队睡,都是就绪态,输出结果可能是乱序态
       print("我是%s进程" %num)

    if __name__ == '__main__':
       start_time = time.time()
       ps = []
       for i in range(5):
           p = Process(target=task,args=(i,))  # 注意此处传参方式!元组形式
           p.start()
           ps.append(p)
       for p in ps:
           p.join()
       print("over")
       print(time.time()-start_time)
    输出结果:
    我是0进程
    我是1进程
    我是2进程
    我是0进程
    我是3进程
    我是1进程
    我是4进程
    我是2进程
    我是3进程
    我是4进程
    over
    5.9241626262664795  #正常应该是2秒多,我电脑太垃圾了,所以卡的接近6秒。


    也可以写成:
    from multiprocessing import Process
    import time
    def task(num):
       print("我是%s进程" %num)
       time.sleep(2) # 排队睡,都是就绪态,输出结果可能是乱序态
       print("我是%s进程" %num)

    if __name__ == '__main__':
       start_time = time.time()
       for i in range(5):
           p = Process(target=task,args=(i,))
           p.start()
       p.join()  # 提高子进程优先级,CPU优先处理子进程,父进程放到后面
       print("over")
       print(time.time()-start_time)
    输出结果为:
    我是0进程
    我是1进程
    我是2进程
    我是0进程
    我是3进程
    我是1进程
    我是4进程
    我是2进程
    我是3进程
    我是4进程
    over
    5.880282878875732

    如果改写成这样:

    from multiprocessing import Process
    import time
    def task(num):
       print("我是%s进程" %num)
       time.sleep(2) # 排队睡,都是就绪态,输出结果可能是乱序态
       print("我是%s进程" %num)

    if __name__ == '__main__':
       start_time = time.time()
       for i in range(5):
           p = Process(target=task,args=(i,))
           p.start()
           p.join()  # 提高子进程优先级,CPU优先处理子进程,父进程放到后面,此处为只有当前子进程结束后,才会继续循环。可以理解为for循环还是属于父进程的内容。
       print("over")
       print(time.time()-start_time)
    输出结果为:
    我是0进程
    我是0进程
    我是1进程
    我是1进程
    我是2进程
    我是2进程
    我是3进程
    我是3进程
    我是4进程
    我是4进程
    over
    14.754556894302368  #正常应该是10秒多一点,我这戴尔垃圾太卡,果然是傻多戴。

     

    Process对象常用属性
    from multiprocessing import Process
    def task(n):
       print('%s is runing' %n)
       time.sleep(n)

    if __name__ == '__main__':
       start_time=time.time()

       p1=Process(target=task,args=(1,),name='任务1')  # 注意传参方式
       p1.start()
       print(p1.pid)  # 获取进程编号
       print(p1.name)  # 获取进程属性
       p1.terminate()  # 结束进程
       p1.join()  # 提高进程优先级
       print(p1.is_alive())  # 判断进程是否存在
       print('主进程')

     

     

  • 相关阅读:
    初识Jmeter
    【性能/接口测试】【Jmeter】保存响应内容
    【Java-jxl插件】【Excel文件读写报错】jxl.read.biff.BiffException: Unable to recognize OLE stream
    随机生成九宫格图形密码-实现
    转载:测试人员的挑战
    运行UI自动化脚本报错,和appium server连接出现问题
    Jenkins容器无法执行docker命令
    【接口测试】使用httpClient获取cookies+携带获取的cookies访问get接口
    【Nginx】安装&环境配置
    【PostMan】批量参数化的用法 之 text/csv
  • 原文地址:https://www.cnblogs.com/realadmin/p/10192721.html
Copyright © 2020-2023  润新知