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 list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value 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() # 进程池中进程执行完毕后再关闭,如果不加,那么程序直接关闭