• python之路——多进程(进程间通信、进程池)


       进程   

      一个进程,包括了代码、数据和分配给进程的资源(内存),在计算机系统里直观地说一个进程就是一个PID。操作系统保护进程空间不受外部进程干扰,即一个进程不能访问到另一个进程的内存。有时候进程间需要进行通信,这时可以使用操作系统提供进程间通信机制。通常情况下,执行一个可执行文件操作系统会为其创建一个进程以供它运行。但如果该执行文件是基于多进程设计的话,操作系统会在最初的进程上创建出多个进程出来,这些进程间执行的代码是一样,但执行结果可能是一样的,也可能是不一样的。

      为什么需要多进程?最直观的想法是,如果操作系统支持多核的话,那么一个执行文件可以在不同的核心上跑;即使是非多核的,在一个进程在等待I/O操作时另一个进程也可以在CPU上跑,提高CPU利用率、程序的效率。

      在Linux系统上可以通过fork()来在父进程中创建出子进程。一个进程调用fork()后,系统会先给新进程分配资源,例如存储数据和代码空间。然后把原来进程的所有值、状态都复制到新的进程里,只有少数的值与原来的进程不同,以区分不同的进程。fork()函数会返回两次,一次给父进程(返回子进程的pid或者fork失败信息),一次给子进程(返回0)。至此,两个进程分道扬镳,各自运行在系统里。

    python调用多进程模块为------->>>multiprocessing

    import multiprocessing,os,time
    
    def func(name):
        print("%s is talking.....[father: %s ;own: %s]"%(name,os.getppid(),os.getpid()))
        # time.sleep(1)
    def f(name):
        func(name)
    
    if __name__ == "__main__":
        func("main")             #父进程调用func
        for i in range(5):
            P = multiprocessing.Process(target=f,args=(i,))    #子进程调用f
            P.start()
    

    其中:os.getppid()  ------->> 输出父进程pid ; os.getpid()  ------->> 输出当前子进程pid

       进程间通讯   

    不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:

    队列(Queues)-----------> 与线程queues方法类似,但不同

    import multiprocessing,time,os,queue
    
    def func(q):
        q.put(["japhi",1,"alex"])
    
    if __name__ == "__main__":
        q = multiprocessing.Queue()     #Queue 能使进程之间进行数据互通
        # q = queue.Queue()       这个方法仅限线程,进程不能用
        q.put("alex")
        p = multiprocessing.Process(target=func,args=(q,))  #实质上是将主进程的q复制了一份给子进程p
        p.start()
        print(q.get())
        print(q.get())
    

    将父进程的队列q传给子进程,实质上是对主进程的q进行了一个复制操作。

    管道(pipe)

    实例化管道后会产生两个值,一个给父进程,一个给子进程,就可以使数据通过管道在父进程和子进程中交互;类似于scoket通信。

    import multiprocessing
    
    def func(con):
        print(con.recv())
        con.send("收到主进程信息")   #子进程收父进程发送的数据
        con.close()
    
    if __name__ == "__main__":
        conn_a,conn_b = multiprocessing.Pipe()    #实例化管道对象,主进程和子进程间可以进行数据通信
        p = multiprocessing.Process(target=func, args=(conn_b,))   #将conn_b给了子进程
        p.start()
        conn_a.send("您好,子进程")  #父进程发送数据
        print(conn_a.recv())
    

    Manager 

    Manager()返回的manager对象控制一个包含Python对象的服务器进程,并允许其他进程使用代理来操作它们。

    Manager()返回的manager将支持类型list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Barrier,Queue,Value and Array。 例如,

    import multiprocessing,os
    
    def mod(d,l):
        d[os.getpid()] =os.getppid()
        l.append(os.getpid())
        print("33[1;32m%s33[0m"%l)
        print("33[1;35m%s33[0m"%d)
    
    if __name__ == "__main__":
        M = multiprocessing.Manager()
        # with multiprocessing.Manager() as M:    与上面的M = multiprocessing.Manager() 作用一致
        dic = M.dict()   #生成一个字典,可在多个进程间共享和传递
        list1 = M.list(range(2))   #生成一个列表,可在多个进程间共享和传递
        p_list = []
        for i in range(5):
            p = multiprocessing.Process(target=mod,args=(dic,list1))
            p.start()
            p_list.append(p)
        for res in p_list:
            res.join()   #等待结果,不然程序会报错
        print(dic)
        print(list1)
    

    进程同步 

    what?

    from multiprocessing import Process, Lock
    
    def f(l, i):
        l.acquire()
        try:
            print('hello world', i)
        finally:
            l.release()
    
    if __name__ == '__main__':
        lock = Lock()
        for num in range(10):
            Process(target=f, args=(lock, num)).start()
    

       进程池   

    进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

    • apply                 串行,且没有回调函数
    • apply_async      并行

    回调函数:指进程里的函数执行完会自动执行回调函数

    import multiprocessing,os,time
    
    def f(name):
        time.sleep(1)
        print("%s from %s"%(name,os.getpid()))
    
    def b(a):
        print("------>>回调")
    
    if __name__ == '__main__':
        # po = multiprocessing.Pool(processes=3)   #进程池中只能同时放入5个进程,可以直接写5
        po = multiprocessing.Pool(3)  #与上面的效果一样
        for i in range(20):
            # po.apply(func = f,args=(i,))    ##串行
            po.apply_async(func=f, args=(i,), callback=b)   #并行,callback是回调,前面f函数执行完执行b
        print("Done")
        po.close()
        po.join()   #进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
    

      

  • 相关阅读:
    2020年秋第四五周-代码规范,结对要求
    2020年秋第四五周-四则运算试题生成
    同时装了WPS和Office新建的时候不知道是哪个文件
    开讲啦郑强演讲:你为什么读大学?
    PC版kindle无法连接网络
    前端编程良好习惯
    教你隐藏盘符,把你的小姐姐藏起来
    腾讯,比你想的更有趣
    U盘之父中国朗科的一生:我曾打败索尼,如今却只能靠收租为生
    动作之概述
  • 原文地址:https://www.cnblogs.com/japhi/p/7094412.html
Copyright © 2020-2023  润新知