zoukankan      html  css  js  c++  java
  • 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('主进程')

     

     

  • 相关阅读:
    Codeforces 1291 Round #616 (Div. 2) B
    总结
    刷新DNS解析缓存+追踪+域名解析命令
    数学--数论--Hdu 5793 A Boring Question (打表+逆元)
    Lucene.net(4.8.0) 学习问题记录六:Lucene 的索引系统和搜索过程分析
    LeetCode 117 Populating Next Right Pointers in Each Node II
    LeetCode 116 Populating Next Right Pointers in Each Node
    test test
    LeetCode 115 Distinct Subsequences
    LeetCode 114. Flatten Binary Tree to Linked List
  • 原文地址:https://www.cnblogs.com/realadmin/p/10192721.html
Copyright © 2011-2022 走看看