1、为什么需要多进程

由于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,只不过换到了多进程的情境。

2、多进程的创建方法

1 import multiprocessing
 2 import time
 3 
 4 
 5 def run(name):
 6     time.sleep(2)
 7     print("hello", name)
 8 
 9 
10 # 多进程
11 if __name__ == "__main__":
12     for i in range(10):
13         p = multiprocessing.Process(target=run, args=("druid %s" % i, ))
14         p.start()
15         p.join()


 

3、进程间通信

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

3.1、Queues

1 from multiprocessing import Queue, Process
 2 import os
 3 
 4 
 5 def f(pq):
 6     pq.put([42, None, "druid"])
 7     print("子进程的父进程ID:", os.getppid())
 8     print("子进程的当前进程ID:", os.getpid())
 9 
10 
11 if __name__ == "__main__":
12     print("父进程ID:", os.getppid())
13     print("当前进程ID:", os.getpid())
14     q = Queue()
15     p = Process(target=f, args=(q,))
16     p.start()
17     print(q.get())
18 
19     p.join()


 

3.2、Pipes

The Pipe() function returns a pair of connection objects connected by a pipe which by default is duplex (two-way). For example:


1 from multiprocessing import Process, Pipe
 2 
 3 
 4 def f(conn):
 5     conn.send("\033[31;1m msg1 from child process\033[0m")
 6     conn.send("\033[31;1m msg2 from child process\033[0m")
 7     print("from parent:", conn.recv())
 8     conn.close()
 9 
10 
11 if __name__ == "__main__":
12     parent_conn, child_conn = Pipe()
13     p = Process(target=f, args=(child_conn,))
14     p.start()
15 
16     print("from child process:", parent_conn.recv())
17     print("from child process:", parent_conn.recv())
18     # print("from child process:", parent_conn.recv())  # 如果接收次数多余发送次数,就会卡住
19     parent_conn.send("\033[32;1m msg from parent process\033[0m")
20 
21     p.join()



 

3.3、Managers

Queues和Pipes只是实现了进程间数据的传递,Managers真正实现了进程间的数据共享,即多个进程可以修改同一份数据。

A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.

A manager returned by Manager() will support types listdictNamespaceLockRLockSemaphoreBoundedSemaphoreConditionEventBarrierQueueValue and Array. For example:


1 from multiprocessing import Process, Manager
 2 import os
 3 
 4 
 5 def f(d, l):
 6     d[os.getpid()] = os.getpid()
 7     l.append(os.getpid())
 8     print(l)
 9 
10 
11 if __name__ == "__main__":
12     with Manager() as manager:
13         d = manager.dict()
14         l = manager.list(range(5))
15         p_list = []  # 用来存放进程
16         for i in range(10):
17             p = Process(target=f, args=(d, l))
18             p.start()
19             p_list.append(p)
20         for res in p_list:
21             res.join()
22 
23         print(d)
24         print(l)


 

4、进程池

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

进程池中有两个方法:

  • apply
  • apply_async

4.1、apply方法


1 from multiprocessing import Pool
 2 import os
 3 import time
 4 
 5 
 6 def func(i):
 7     time.sleep(2)
 8     print("子进程ID:", os.getpid())  # 每隔两秒打印一次结果
 9     return i+100
10 
11 
12 if __name__ == "__main__":
13     pool = Pool(processes=5)  # 允许进程池同时放入5个进程
14     print("主进程ID:", os.getpid())
15     for i in range(10):
16         pool.apply(func=func, args=(i,))  # 串行
17 
18     print("end")
19     pool.close()
20     pool.join()  # 进程池中进程执行完毕后再关闭,如果不加,那么程序直接关闭

 

4.2、apply_async方法

1 from multiprocessing import Pool
 2 import os
 3 import time
 4 
 5 
 6 def func(i):
 7     time.sleep(2)
 8     print("子进程ID:", os.getpid())
 9     return i+100
10 
11 
12 if __name__ == "__main__":
13     pool = Pool(processes=5)  # 允许进程池同时放入5个进程
14     print("主进程ID:", os.getpid())
15     for i in range(10):
16         pool.apply_async(func=func, args=(i,))  # 并行
17 
18     print("end")  # 先打印主进程ID和"end",然后每次打印5个子进程ID
19     pool.close()
20     pool.join()  # 进程池中进程执行完毕后再关闭,如果不加,那么程序直接关闭



 

4.3、apply_async+回调函数

1 from multiprocessing import Pool
 2 import os
 3 import time
 4 
 5 
 6 def func(i):
 7     time.sleep(2)
 8     print("子进程ID:", os.getpid())
 9     return i+100
10 
11 
12 def bar(arg):
13     print("--->exec done: %s, SubPID:%s" % (arg, os.getpid()))
14 
15 
16 if __name__ == "__main__":
17     pool = Pool(processes=5)  # 允许进程池同时放入5个进程
18     print("主进程ID:", os.getpid())
19     for i in range(10):
20         pool.apply_async(func=func, args=(i,), callback=bar)  # callback为回调函数,前面的函数执行完了才会执行回调函数
21 
22     print("end")
23     pool.close()
24     pool.join()  # 进程池中进程执行完毕后再关闭,如果不加,那么程序直接关闭