• Python笔记_第四篇_高阶编程_进程、线程、协程_1.进程


    1. 多任务原理

      现代操作系统,像win,max os x,linux,unix等都支持多任务。

      * 什么叫做多任务?

        操作系统可以同时运行多个任务。

      * 单核CPU实现多任务原理?

        操作系统轮流让各个任务交替执行,比如QQ执行2us,切换到微信,再执行2us,在切换到其他应用,再执行2us... ...,如此交替往复。表面上看每个任务反复执行下去,但是CPU调度执行速度太快了,导致我们感觉就像所有任务都同时执行一样。

      * 多核CPU实现多任务原理?

        真正的并行执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU核心数量,所以操作系统会把很多任务轮流调度到每个核心上执行。

      * 并发和并行的概念:

        并发:看上去是一起执行,任务多于CPU核心数。

        并行:真正一起执行,任务数小于CPU核心数。

      * 实现多任务的方式:

        * 多进程模式

        * 多线程模式

        * 协程模式

        * 多进程 + 多线程模式。

      这里讲解前三种,另外,这部分内容还会提及到GPU的多任务原理(使用GPU强大的浮点运算功能,实现算法的快速运算)

    2. 进程

      进程:对于操作系统而言,一个任务就是一个进程。进程是系统中程序执行和资源分配的基本单位。每个进程都有自己的数据段、代码段、堆栈段。

      2.1 从一个单任务现象说起

      如下代码:

    from time import sleep
    
    def run():
        while True:
            print("Thomas is a nice man")
            sleep(1.2)
    
    if __name__ == '__main__':
        while True:
            print("Thomas is a good man")
            sleep(1)
    
    
    run() #不会执行到run方法,只有上面的while循环结束才可以执行
    # Thomas is a good man
    # Thomas is a good man
    # Thomas is a good man
    # Thomas is a good man

      我们发现代码只会执行good man,因为只有上面的循环结束才可以执行。其实在最初的代码编程中,就是一种这样的顺序执行方式。

      2.2 启动进行实现多任务:multirprocessing库

        我们通过进程库,创建一个进程,把想要执行的代码“丢入”进程当中去执行。

        先看代码:

    r"""
    multiprocessing 库:
    跨平台版本的多进行实现的模块。提供了Process类来代表一个进程对象。
    
    """
    from multiprocessing import Process
    from time import sleep
    import os
    
    def run(str):
        while True:
            # os.getpid()获得当前进程的id号码
            # os.getppdi()获得当前进行的父进程id号码
            print("Jerry is a %s man: 子进程%s--父进程%s" %(str,os.getpid(),os.getppid()))
            sleep(1.2)
    
    if __name__ == '__main__':
        print("主(父)进程启动!--%s" %(os.getpid()))
        # 创建一个子进程:
        # target说明进程执行的任务
        p = Process(target=run,args=("nice",))  # 元组如果只有一个,后面要逗号
        # 启动进程
        p.start()
    
        while True:
            print("Thomas is a good man")
            sleep(1)
    
    # 不创建的进程都是主进程,首先执行的。
    # 主(父)进程启动!--4368
    # Thomas is a good man
    # Jerry is a nice man: 子进程3280--父进程4368
    # Thomas is a good man

      说明1:我们发现主进程是一个默认进程,创不创建(不用创建)都会有一个父进程的存在。

      说明2:os.getpid()方法是获得当前的进程id号码,process id。

      说明3:p.start()表示的是启动子进程。

      说明4:在创建进程的时候,参数是一个元组的形式,如果只有一个,后面要用逗号进行分割。

      说明5:我们发现,子进程的运行过程,父进程不受子进程的影响。如果我们想在整个程序的运行过程中,父进程开启,等到子进程完全结束后,再结束父进程。如下:2.3 父进程的先后顺序。

      2.3 父进程的先后顺序

        先看代码:

    from multiprocessing import Process
    from time import sleep
    
    def run():
        while True:
            print("启动子进程")
            sleep(3)
            print("子进程结束")
    
    if __name__ == '__main__':
        print("主(父)进程启动!")
    
        p = Process(target=run)  # 元组如果只有一个,后面要逗号
        p.start()
    
        # 父进程的结束不影响子进程
        # 如果我们让父进程等待子进程结束在执行父进程结束。
        p.join()
        
        print("父进程结束!")

    主(父)进程启动!
    启动子进程
    子进程结束
    启动子进程
    子进程结束

      说明1:我们发现,我们通过p.join()的方法让父进程挡在子进程的门外,只有当子进程结束了,父进程再能结束(我们让子进程一直运行,这样父进程就一直结束不了了。),但是比如说我们有一个全局变量,这个全局变量在进程中是如何变化的?

      2.4 全局变量在多个进程中不能共享

        看代码:

    from multiprocessing import Process
    
    # 如果我们有一个全局变量
    num = 100
    
    def run():
        print("子进程开始")
        global num
        num += 1
        print(num)
        print("子进程结束")
    
    if __name__ == '__main__':
        print("父进程开始")
        p = Process(target=run)  # 加入括号编程函数的执行了。
        p.start()
        p.join()
    
        # 在兄弟进程中全局变量也是不会相互影响的
        # p2 = Process(target=run)  # 加入括号编程函数的执行了。
        # p2.start()
        # p2.join()
    
        # 在子进程中修改全局变量对父进程中的全局变量没有影响。
        # 在创建子进程时对全局变量做了一个备份,父进程与子进程中的num是完全不同的两个变量。
        print("父进程结束--%d" % num)
    
    父进程开始
    子进程开始
    101
    子进程结束
    父进程结束--100

      说明1:在子进程中修改了全局变量对父进程中的全局变量没有影响。

      说明2:在创建子进程时对全局变量做了一个备份,父进程与子进程中的num是两个完全不同的变量了。

      说明3:现在我们是通过进程的方法启动,阻塞进行进程的创建。其实在实际的运行过程中,我们想CPU的多进程一起运行,还可以通过进程池的方式。

      2.5 启动大量子进程(进程池Pool)

        进程池,好比创建一个池子,把进程丢入到池子中,让电脑自动去分配进程,不用自己单个去创建。

        看代码:

    from multiprocessing import Pool
    import time,os,random
    
    def run(name):
        print("子进程%d启动,PID=%s" %(name,os.getpid()))
        start = time.time()
        time.sleep(random.choice([1,2,3]))
        end = time.time()
        print("子进程%d结束,PID=%s, 耗时%.2f" % (name, os.getpid(),end-start))
    
    if __name__ == '__main__':
        print("主进程启动")
    
        # 创建多个进程
        # 进程池
        # 表示可以同时执行的进程数量
        # Pool默认是大小CPU核心数
        pp = Pool()
    
        # 创建进程放入进程池,统一管理
        for i in range(5):
            pp.apply_async(run,args=(i,))
    
        # 在调用join之前必须先调用close,调用close之后不能再继续添加新的进程了。
        pp.close()
        # 进程池对象调用的join,会等待进程池中所有的子进程结束完毕,再去执行父进程。
        pp.join()
    
        print("主进程结束")
    
    # 主进程启动
    # 子进程0启动,PID=9172
    # 子进程1启动,PID=2996
    # 子进程0结束,PID=9172, 耗时2.00
    # 子进程2启动,PID=9172
    # 子进程1结束,PID=2996, 耗时2.00
    # 子进程3启动,PID=2996
    # 子进程2结束,PID=9172, 耗时2.00
    # 子进程4启动,PID=9172
    # 子进程3结束,PID=2996, 耗时2.00
    # 子进程4结束,PID=9172, 耗时2.00
    # 主进程结束

      说明1:创建进程池使用的Pool模块。

      说明2:把程序丢入进程池,我们用的是pp.apply_async这个方法。

      说明3:调用join之前必须先调用close,调用close之后不能再继续添加新的进程了。

      说明4:进程池对象调用join,会等待进程池中所有的子进程技术完毕,再去执行父进程。

      2.6 举例:通过多进程来实现文件的拷贝

        代码如下:

    from multiprocessing import Pool
    import time,os
    
    def copyFile(path,toPath):
        fr = open(path,"r")
        fw = open(toPath,"w")
    
        context = fr.read()
        fr.write(context)
        fr.close()
        fw.close()
    
    path = r""
    toPath = r""
    # 读取path下的所有文件
    filesList = os.listdir(path)
    
    # 启动for循环处理每个文件
    starttime = time.time()
    pp = Pool()
    for fileName in filesList:
        pp.apply_async(copyFile,args=(os.path.join(path,fileName),os.path.join(toPath,fileName)))
    endtime = time.time()
    print("总耗时%.4f" %(endtime-starttime))

      2.7 进程间的通信(Queue方法/队列方法):

        先看代码:

    from multiprocessing import Process,Queue
    import os,time
    
    def write(q):
        print("启动写write进程%s" % os.getpid())
        for chr in ["A","B","C","D"]:
            q.put(chr)
        time.sleep(1)
        print("结束写write进程%s" % os.getpid())
    
    
    def read(q):
        print("启动读read进程%s" % os.getpid())
        while True:
            value = q.get(True)
            print("value=" + value)
    
        print("结束读read进程%s" % os.getpid())
    
    
    if __name__ == '__main__':
        # 父进程创建队列,并传递给子进程
        q = Queue()
        pw = Process(target=write,args=(q,))
        pr = Process(target=read,args=(q,))
    
        pw.start()
        pr.start()
    
        #
        pw.join()
        # pr进程是一个死循环,无法等待其结束,只能强行结束
        pr.terminate()
    
        print("父进程结束")
    
        while True:
            pass
    
    启动写write进程7296
    启动读read进程864
    value=A
    value=B
    value=C
    value=D
    结束写write进程7296
    父进程结束

       说明1:我们在main函数中创建了一个进程的队列(队列一共有4中,这里使用的是进程的队列),通过两个读写进程的相互交互,实现了读写的方式。

       说明2:如果一个进程是一个四选好,无法等待其结果结束,使用.pr.terminate()的方法进行强行的关闭。

  • 相关阅读:
    Python环境搭建
    Python简介
    第一个Java程序
    shiro实战(1)--web
    JDBC释放数据库连接
    IDEA(ideaIU) v2019.2.2详细安装破解教程
    ubuntu通过代理设置update源
    virtualbox FAIL(0x80004005) VirtualBox VT-x is not available (VERR_VMX_NO_VMX)
    Dubbo入门实例
    jsp模板
  • 原文地址:https://www.cnblogs.com/noah0532/p/10938771.html
Copyright © 2020-2023  润新知