有的时候一个任务需要进行大量的网络、磁盘或数据库请求,比如后台抓取网页,比如离线数据统计等,完成任务需要多次迭代,每次都会产生网络IO、磁盘IO或数据库连接等待,导致任务执行时间很长。但是,任务又有一个特点,迭代之间不需要顺序执行,这就是多线程/多进程非常适合的场景。
在python里,因为GIL的限制,导致没有真正的多线程,所以ThereadPool也在官网提示使用multiprocessing来代替。
This module is OBSOLETE and is only provided on PyPI to support old projects that still use it.
Please DO NOT USE IT FOR NEW PROJECTS!
Use modern alternatives like the multiprocessing module in the standard library or even an asynchroneous approach with asyncio.
multiprocessing 的简单用法:
from multiprocessing import Pool
def f(x):
return x*x
if __name__ == '__main__':
with Pool(5) as p:
print(p.map(f, [1, 2, 3]))
这种方式,采用的依然是同步顺序执行的方式,发挥多进程威力的异步方式如下:
from multiprocessing import Pool
import time
def f(x):
time.sleep(1)
return x,x*x
if __name__ == '__main__':
res_list = []
# apply
with Pool(5) as p:
for i in xrange(1,20):
res = pool.apply_async(f,[i])
res_list.append(res)
# print
for res in res_list:
print(res.get())
pool.close()
pool.join()
需要特别注意的几个地方:
- res.get() 需要在进程调用完成后,统一获取,否则就是同步方式了
- pool.join() 使主进程等待所有子进程完成后,再退出
- 需要在join前调用pool.close()
也可以直接定义Processing
from multiprocessing import Process
import os
import time
def info(title):
print(title)
print('module name:', __name__)
print('parent process:', os.getppid())
print('process id:', os.getpid())
def f(name):
time.sleep(1)
info('function f')
print('hello', name)
if __name__ == '__main__':
info('main line')
for i in xrange(10):
p = Process(target=f, args=(i,))
p.start()
# p.join()
备注:
- 通过os.getppid()和os.getpid() 获取父进程及当前进程的ID
- 如果希望线程顺序执行,可调用p.join()