众所周知,因为python(Cpython)的全局锁(GIL)问题存在,致使Thread也就是线程的并行并不可实现。 multiprocessing 模块采用多进程而不是多线程的方式实现并行,解决了GIL的问题,必定程度上使情况获得了缓解。html
然而,Multiprocess自己依然有一些功能上的瓶颈。其中一个重要的是:进程之间不能共享内存(线程间则能够共享内存)。这意味着在进程间交换数据的时候,须要把数据打包、传递,解包。在python的语境下就是:python
"pickle from main process to the subprocess;git
depickle from subprocess to an object in memory;github
pickle and return to the main process;多线程
depickle from main process and return to memory"dom
(具体详见这个问题下的吐槽)函数
所以, 在须要在进程间共享巨大的数据包的时候,多进程的表现还不如单进程。测试
除此以外,当须要运行的程序自己不是计算密集型而是是IO密集型,多进程所增长的读写会抵消掉运算速度的增益;若是程序复杂度根本不须要用并行来解决,那么创建进程(池)的时间极可能比运行程序自己还要慢;另外,在进程池 multiprocessing.Pool(n) 的 n 的选择上,若是选择了多于当前CPU的核心数目的数字( multiprocessing.cpu_count() ),那么在进程之间切换的功夫会大大拉低效率。ui
创建对线程和进程关系的直观印象,可参考这篇文章。spa
快速而完整地了解python的全局锁(GIL)问题,参考这篇不错的博客。
为了解 multiprocess 的使用,我作了一些测试,测试环境是4核的Macbook Air。以下:
from multiprocessing import Process, Manager, Pool
1 def f(l): 2 l.reverse() 3 return 4 5 def main(): 6 l1 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 7 l2 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 8 l3 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 9 l4 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 10 l5 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 11 l6 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 12 l7 = [random.randrange(0, 100000, 1) for i in range(0, 100000)] 13 s = time.time() 14 for l in [l1, l2, l3, l4, l5, l6, l7]: 15 f(l) 16 print "%s seconds" % (time.time() - s) 17 s = time.time() 18 map(f, [l1, l2, l3, l4, l5, l6, l7]) 19 print "%s seconds" % (time.time() - s) 20 p = Pool(4) 21 s = time.time() 22 p.map(f, [l1, l2, l3, l4, l5, l6, l7]) 23 print "%s seconds" % (time.time() - s) 24 return
也就是分别测试 f() 对 l1, l2, l3, l4, l5, l6, l7 7个列表的操做时间。先是循环的依次操做,再是python中很是好用的 map() 函数,最后是 multiprocessing 的进程池 multiprocessing.Pool.map() ——进程池中创建了4个 worker process , 也就是说,接下来的任务会被随机地分配给4个进程来完成。
每次操做以前都从新计时,获得了这样的结果:
>>> main() 0.00250101089478 seconds 0.000663995742798 seconds 0.907639980316 seconds
多进程出奇得慢。而 map() 相对于循环操做有很大的效率提高。
因此并非全部任务都适合多进程(至于列表的倒置为何不适合多进程,我并不明白。。)。在最近的实验中,我须要找到提高效率的方法,之后的测试会陆续放上来。
实验的时候,原本用 Pool(n) ,可是始终卡在了 racquire() 这里(ctrl-c终止后停在的地方),因而改用 Process() ,就能够运行了。可是运行到中间的某一行后,原本设定的4个进程变成了只有1个进程在运行,缘由是内存超了。。
另外还有一个让人头疼的问题。在用 Process()的时候,若是有一个进程灭了,那么你不会收到任何提示 —— 没错,就是悄无声息地没了。。