该模块的中文开发文档地址:https://docs.python.org/zh-cn/3.7/library/multiprocessing.html
什么是multiprocessing
multiprocessing
是一个用与 threading
模块相似API的支持产生进程的包。 multiprocessing
包同时提供本地和远程并发,使用子进程代替线程,有效避免 Global Interpreter Lock 带来的影响。因此, multiprocessing
模块允许程序员充分利用机器上的多个核心。Unix 和 Windows 上都可以运行。
multiprocessing中的Pool类
Pool 类表示一个工作进程池。它具有允许以几种不同方式将任务分配到工作进程的方法。
创建进程池:
multiprocessing.pool.
Pool
([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
processes 是要使用的工作进程数目。如果 processes 为 None
,则使用 os.cpu_count()
返回的值。
如果 initializer 不为 None
,则每个工作进程将会在启动时调用 initializer(*initargs)
。
maxtasksperchild 是一个工作进程在它退出或被一个新的工作进程代替之前能完成的任务数量,为了释放未使用的资源。默认的 maxtasksperchild 是 None
,意味着工作进程寿与池齐。
context 可被用于指定启动的工作进程的上下文。通常一个进程池是使用函数 multiprocessing.Pool()
或者一个上下文对象的 Pool()
方法创建的。在这两种情况下, context 都是适当设置的。
注意,进程池对象的方法只有创建它的进程能够调用。
Pool类常用到的四个方法:
apply
(func[, args[, kwds]])
使用 args 参数以及 kwds 命名参数调用 func , 它会返回结果前阻塞。这种情况下,apply_async()
更适合并行化工作。另外 func 只会在一个进程池中的一个工作进程中执行。
apply_async
(func[, args[, kwds[, callback[, error_callback]]]])
apply()
方法的一个变种,返回一个结果对象。
如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。
如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。
回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。
map
(func, iterable[, chunksize])
内置 map()
函数的并行版本 (但它只支持一个 iterable 参数,对于多个可迭代对象请参阅 starmap()
)。 它会保持阻塞直到获得结果。
这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。
注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 imap()
或 imap_unordered()
并且显示指定 chunksize 以提升效率。
map_async
(func, iterable[, chunksize[, callback[, error_callback]]])
和 map()
方法类似,但是返回一个结果对象。
如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。
如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。
回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。
说人话就是:
1:apply方法是阻塞的方法。
假设现在设置了进程池为4个(Pool(4)),也就是共有1,2,3,4四个子进程,阻塞的意思是,子进程1执行完毕后,子进程2才能继续进行。
一个例子:
import multiprocessing
import time
def get(seconds):
time.sleep(seconds)
print("process name: " + multiprocessing.current_process().name)
if __name__ == "__main__":
start = time.time()
pool = multiprocessing.Pool(5)
for i in range(10):
pool.apply(get, args=(1,))
pool.close()#close方法关闭进程池,此时不允许新的子进程进入。close方法必须在join方法调用前调用
pool.join() # 主进程被阻塞,等子进程全部执行完毕后再执行print
print("test end",time.time()-start)
输出为:
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-4
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-5
process name: SpawnPoolWorker-2
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-4
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-5
process name: SpawnPoolWorker-2
test end 10.283506631851196
可见,最后共花销了10秒,相当于是一个进程在跑,就是因为apply是阻塞的。必须一个子进程结束后,下一个子进程才能进行。所有几乎不使用apply方法。
2:apply_async 是异步非阻塞的方法。
假设现在设置了进程池为4个(Pool(4)),也就是共有1,2,3,4四个子进程,非阻塞的意思是,子进程1执行的同时,子进程2、3、4也同步执行。
一个例子:
import multiprocessing
import time
def get(seconds):
time.sleep(seconds)
print("process name: " + multiprocessing.current_process().name)
if __name__ == "__main__":
start = time.time()
pool = multiprocessing.Pool(5)
for item in range(10):
pool.apply_async(get, args=(1,)) # apply_async方法,通过args参数传参,进程之前不阻塞,真正意义上的并行
pool.close()#close方法关闭进程池,此时不允许新的子进程进入。close方法必须在join方法调用前调用
pool.join() # 主进程被阻塞,等子进程全部执行完毕后再执行print
print("test end",time.time()-start)
输出为:
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-2
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-4
process name: SpawnPoolWorker-5
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-2
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-4
process name: SpawnPoolWorker-5
test end 2.3337607383728027
可以看到,我们设置了5个进程池,一次运行5个进程,最后耗时2秒。这就是异步非阻塞的功效。
3:map和map_async功能差不多,主要是后者有callback 和errorcallback ,便于处理返回结果
实行过程中,效率是一样的。
例子如下:
map_async
import multiprocessing
import time
def get(seconds):
time.sleep(seconds)
print("process name: " + multiprocessing.current_process().name)
if __name__ == "__main__":
start = time.time()
pool = multiprocessing.Pool(5)
pool.map_async(get,[1 for i in range(10)])
pool.close()#close方法关闭进程池,此时不允许新的子进程进入。close方法必须在join方法调用前调用
pool.join() # 主进程被阻塞,等子进程全部执行完毕后再执行print
print("test end",time.time()-start)
输出结果:
C:\Python36\python.exe E:/JOB/working/test.py
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-4
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-2
process name: SpawnPoolWorker-5
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-4
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-2
process name: SpawnPoolWorker-5
test end 2.3187944889068604
map:
import multiprocessing
import time
def get(seconds):
time.sleep(seconds)
print("process name: " + multiprocessing.current_process().name)
if __name__ == "__main__":
start = time.time()
pool = multiprocessing.Pool(5)
pool.map(get,[1 for i in range(10)])
pool.close()#close方法关闭进程池,此时不允许新的子进程进入。close方法必须在join方法调用前调用
pool.join() # 主进程被阻塞,等子进程全部执行完毕后再执行print
print("test end",time.time()-start)
输出为:
process name: SpawnPoolWorker-2
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-5
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-4
process name: SpawnPoolWorker-2
process name: SpawnPoolWorker-1
process name: SpawnPoolWorker-5
process name: SpawnPoolWorker-3
process name: SpawnPoolWorker-4
test end 2.24300217628479
可以看到,两种方式的效率没有差别,如果对callback有需求的,可以使用map_async