衆所周知,因爲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()的時候,若是有一個進程滅了,那麼你不會收到任何提示 —— 沒錯,就是悄無聲息地沒了。。