因为GIL的存在,Python的多线程是一种伪并发,因为GIL规定一个进程中同一时间只能有一个线程使用CPU,这就使得多线程只能共用一个CPU。所以如果我们想要使用多核CPU进行并发计算,就需要使用Python的多进程编程。
这里主要通过multiprocessing库来实现
multiprocessing文档
需要注意的一点是,Python的多进程程序应该在
if __name__ == '__main__'
中运行
通过创建一个 Process 对象然后调用它的 start() 方法来生成进程。
from multiprocessing import Process
import os
def info(title):
print(title)
print('module name:', __name__)
print('parent process:', os.getppid())
print('process id:', os.getpid())
def f(name):
info('function f')
print('hello', name)
if __name__ == '__main__':
info('main line')
p = Process(target=f, args=('bob',))
# 启动进程
p.start()
# 阻塞主进程,等待子进程结束
p.join()
当你想要启动多个子进程并阻塞主进程
import multiprocessing
def worker(num):
"""process worker function"""
print('Worker:', num)
return
if __name__ == '__main__':
processes = []
for i in range(5):
p = multiprocessing.Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
multiprocessing提供了队列和管道两种进程间通信方式
先进先出。
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
管道是双工的。
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello'])
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, 'hello']"
p.join()
请注意,如果两个进程(或线程)同时尝试读取或写入管道的 同一 端,则管道中的数据可能会损坏。
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()
进程池运行将进程放入进程池并且限定同时运行的最大进程数。
进程池对象的方法只有创建它的进程能够调用。
注意
multiprocessing.pool 对象具有需要正确管理的内部资源
(像任何其他资源一样),具体方式是将进程池用作上下文管理器,或者手动调用 close() 和 terminate()。
未做此类操作将导致进程在终结阶段挂起。
import multiprocessing
import time
def worker(num):
"""thread worker function"""
print('Worker:', num)
time.sleep(1)
return
if __name__ == '__main__':
# 限定进程数为4
with multiprocessing.Pool(processes=4) as pool:
results = [pool.apply_async(worker, args=(i,)) for i in range(5)]
pool.join()
output = [p.get() for p in results]
print(output)
使用map
import multiprocessing
def worker(num):
"""thread worker function"""
print('Worker:', num)
return num * 2
if __name__ == '__main__':
with multiprocessing.Pool(processes=4) as pool:
results = pool.map(worker, range(5))
pool.join()
print(results)