• Python进阶(4)_进程与线程 (python并发编程之多进程)


    一、python并发编程之多进程

    1.1 multiprocessing模块介绍

      由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。

      multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。

      该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。

    构造方法:

    Process([group [, target [, name [, args [, kwargs]]]]])

      group: 线程组,目前还没有实现,库引用中提示必须是None; 
      target: 要执行的方法; 
      name: 进程名; 
      args/kwargs: 要传入方法的参数。

    实例方法:

      is_alive():返回进程是否在运行。

      join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

      start():进程准备就绪,等待CPU调度

      run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

      terminate():不管任务是否完成,立即停止工作进程

    属性:

      daemon:和线程的setDeamon功能一样

      name:进程名字。

      pid:进程号。

    方法介绍:

    p1=Process(target=foo,args=('p1',))    开启子进程
    p.start():启动进程,并调用该子进程中的p.run() 
    p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法  
    
    p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
    p.is_alive():如果p仍然运行,返回True
    
    p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

    linux中,主进程的全局变量,子进程能用

    window中,主进程的全局变量,子进程不能用能用

    1.2 创建并开启子进程的两种方式

    通过函数:

    #函数方式
    def foo(name):
        print('%s starting'%name)
        time.sleep(random.randrange(1,5))
        print('%s is end'%name)
    
    if __name__ == '__main__':
        print('主进程starting')
    
        p1=Process(target=foo,args=('p1',))
        p2=Process(target=foo,args=('p2',))
        p3=Process(target=foo,args=('p3',))
    
        # p1.daemon = True
        # p2.daemon = True
        # p3.daemon = True
    
        p1.start()
        p2.start()
        p3.start()
    
        # p1.join()
        # p2.join()
        # p3.join()
    
        # time.sleep(1)
        print('主进程ending')
    

    通过类的方式:

    #用类的方式创建子进程
    class MyProcess(Process):
        def __init__(self,name):
            super().__init__()
            # self.name=name
    
        def run(self):
            print('%s starting' % self.name)
            time.sleep(random.randrange(1, 5))
            print('%s id is %s' % (self.name,self.pid))
            print('%s is end'%self.name)
    
    if __name__ == '__main__':
        print('主进程starting')
        for i in range(4):
            p = MyProcess(str(i))
            p.start()
    
        print('主进程ending')
    

    1.3 Process对象的其他方法或属性

    #进程对象的其他方法一:terminate,is_alive
    from multiprocessing import Process
    import time
    import random
    
    class Piao(Process):
        def __init__(self,name):
            self.name=name
            super().__init__()
    
        def run(self):
            print('%s is piaoing' %self.name)
            time.sleep(random.randrange(1,5))
            print('%s is piao end' %self.name)
    
    
    p1=Piao('egon1')
    p1.start()
    
    p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
    print(p1.is_alive()) #结果为True
    
    print('开始')
    print(p1.is_alive()) #结果为False
    进程对象的其他方法一:terminate,is_alive
    #进程对象的其他方法二:p.daemon=True,p.join
    from multiprocessing import Process
    import time
    import random
    
    class Piao(Process):
        def __init__(self,name):
            self.name=name
            super().__init__()
        def run(self):
            print('%s is piaoing' %self.name)
            time.sleep(random.randrange(1,3))
            print('%s is piao end' %self.name)
    
    
    p=Piao('egon')
    p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程死,p跟着一起死
    p.start()
    p.join(0.0001) #等待p停止,等0.0001秒就不再等了
    print('开始')
    #进程对象的其他方法二:p.daemon=True,p.join

    注:p.join(),是父进程在等p的结束,是父进程阻塞在原地,而p仍然在后台运行

    #进程对象的其他属性:name,pid
    from multiprocessing import Process
    import time
    import random
    class Piao(Process):
        def __init__(self,name):
            # self.name=name
            # super().__init__() #Process的__init__方法会执行self.name=Piao-1,
            #                    #所以加到这里,会覆盖我们的self.name=name
    
            #为我们开启的进程设置名字的做法
            super().__init__()
            self.name=name
    
        def run(self):
            print('%s is piaoing' %self.name)
            time.sleep(random.randrange(1,3))
            print('%s is piao end' %self.name)
    
    p=Piao('egon')
    p.start()
    print('开始')
    print(p.pid) #查看pid
    #进程对象的其他属性:name,pid

    1.2 进程间通讯 

    1.2.1进程间通信(IPC)方式一:队列(推荐使用)

    from multiprocessing import Process
    import queue
    
    def f(q,n):
        q.put(n*n+1)
        print("son process",id(q))
    
    if __name__ == '__main__':
    
        q = queue.Queue()  
        print("main process",id(q))
    
        for i in range(3):
            p = Process(target=f, args=(q,i))
            p.start()
    
        print(q.get())
        print(q.get())
        print(q.get())
    

    1.2.2进程间通信(IPC)方式二:管道(不推荐使用,了解即可)

    from multiprocessing import Pipe,Process
    
    def foo(sk):
        sk.send("hello world")
        print(sk.recv())
    
    if __name__ == '__main__':
        sock, conn = Pipe()
        p=Process(target=foo,args=(sock,))
        p.start()
    
        print(conn.recv())
        conn.send("hi son")
    
    >>:hello world
           hi son
    

      Pipe()返回的两个连接对象代表管道的两端。 每个连接对象都有send()和recv()方法(等等)。 请注意,如果两个进程(或线程)尝试同时读取或写入管道的同一端,管道中的数据可能会损坏。

    1.2.3进程间通信方式三:共享数据(不推荐使用,了解即可)

      Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。可以通过Manager实现数据共享,

      从数据安全角度看,进程间通信应该尽量避免使用本节所讲的共享数据的方式

    用manager实现进程共享数据

    from multiprocessing import Process,Manager
    
    def foo(d,l,i):
        d[i] = i
        d['egon'] = 'egon'
        l.append(i**2)
    
    if __name__ == '__main__':
        # manager = Manager()
        dic = Manager().dict()
        Mlist = Manager().list([11,22,33])
        l=[]
        for i in range(5):
            p=Process(target=foo,args=(dic,Mlist,i))
            p.start()
            l.append(p)
    
        for j in l:
            j.join()
    
        print(dic)
        print(Mlist)
    

    1.3 进程池(重点学习)

      开多进程的目的是为了并发,如果有多核,通常有几个核就开几个进程,进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行),但很明显需要并发执行的任务要远大于核数,这时我们就可以通过维护一个进程池来控制进程数目,比如httpd的进程模式,规定最小进程数和最大进程数...

      进程池(Pool)可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程

        在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。

    进程池实例:

    from multiprocessing import Pool
    import time
    
    def foo(n):
        print(n)
        time.sleep(1)
    
    if __name__ == '__main__':
    
        pool_obj=Pool(5)
    
        for i in range(100):
            pool_obj.apply_async(func=foo,args=(i,))
    
        pool_obj.close()
        pool_obj.join()
    
        print("ending")
    

      

  • 相关阅读:
    45 岁,还写代码吗?
    给你 8 个接私活的网站
    一文回顾 Java 入门知识(下)
    5 种前途迷茫的编程语言
    JVM 内存的结构模型、堆与堆栈原理、对象在内存中的结构
    mysql 索引是否能提高UPDATE,DELETE,INSERT 处理速度
    【诈尸】【游戏】多人联机游戏推荐
    250.统计同值子树
    366. 寻找二叉树的叶子节点
    156.上下翻转二叉树
  • 原文地址:https://www.cnblogs.com/hedeyong/p/7211639.html
Copyright © 2020-2023  润新知