python之多進程和進程池(multiprocessing庫)

環境:win7+python2.7

一直想學習多進程或多線程,但之前只是單純看一點基礎知識還有簡單的介紹,無法理解怎么去應用,直到前段時間看了github的一個爬蟲項目涉及到多進程,多線程相關內容,一邊看一邊百度相關知識點,現在把一些相關知識點和一些應用寫下來做個記錄.

首先說下什么是進程:進程是程序在計算機上的一次執行活動,當運行一個程序的時候,就啟動了一個進程.而進程又分為系統進程和用戶進程.只要是用于完成操作系統的各種功能的進程就是系統進程,它們就是處于運行狀態下的操作系統本身;而所有由你啟動的進程都是用戶進程。進程是操作系統進行資源分配的單位。

直觀點說,在任務管理器的用戶名上標明system的是系統進程,標明administrator的是用戶進程,另外net是網洛,lcacal service是本地服務,關于進程更加具體的信息可以百科,這里得省點力氣,不然收不回了.

一.多進程的簡單使用

如圖,multiprocessing有多個函數,很多我也還沒去了解,這里只講我目前了解的.


進程創建:Process(target=主要運行的函數,name=自定義進程名稱可不寫,args=(參數))

方法:is_alive():判斷進程是否存活
join([timeout]):子進程結束再執行下一步,timeout為超時時間,有時進程遇到阻塞,為了程序能夠運行下去而設置超時時間
run():如果在創建Process對象的時候不指定target,那么就會默認執行Process的run方法
start():啟動進程,區分run()
terminate():終止進程,關于終止進程沒有這么簡單,貌似用psutil包會更好,有機會以后了解更多再寫下。
其中,Process以start()啟動某個進程。

屬性:authkey: 在文檔中authkey()函數找到這么一句話:Set authorization key of process設置過程的授權密鑰 ,目前沒找到相關應用實例,這個密鑰是怎么用的呢?文章不提
daemon:父進程終止后自動終止,且自己不能產生新進程,必須在start()之前設置
exitcode:進程在運行時為None、如果為–N,表示被信號N結束
name:進程的名字,自定義
pid:每個進程有唯一的PID編號。

1.Process(),start(),join()

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a=time.time()
    p1=Process(target=fun1,args=(4,))
    p2 = Process(target=fun2, args=(6,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    b=time.time()
    print 'finish',b-a

這里一共開了兩個進程,p1和p2,arg=(4,)中的4是fun1函數的參數,這里要用tulpe類型,如果兩個參數或更多就是arg=(參數1,參數2...),之后用start()啟動進程,我們設置等待p1和p2進程結束再執行下一步.來看下面的運行結果,fun2和fun1基本在同一時間開始運行,當運行完畢(fun1睡眠4秒,同時fun2睡眠6秒),才執行print 'finish',b-a語句

this is fun2 Mon Jun 05 13:48:04 2017
this is fun1 Mon Jun 05 13:48:04 2017
fun1 finish Mon Jun 05 13:48:08 2017
fun2 finish Mon Jun 05 13:48:10 2017
finish 6.20300006866

Process finished with exit code 0

我們再來看下start()與join()處于不同位置會發生什么

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a=time.time()
    p1=Process(target=fun1,args=(4,))
    p2 = Process(target=fun2, args=(6,))
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    b=time.time()
    print 'finish',b-a

結果:

this is fun1 Mon Jun 05 14:19:28 2017
fun1 finish Mon Jun 05 14:19:32 2017
this is fun2 Mon Jun 05 14:19:32 2017
fun2 finish Mon Jun 05 14:19:38 2017
finish 10.1229999065

Process finished with exit code 0

看,現在是先運行fun1函數,運行完畢再運行fun2接著再是print 'finish',即先運行進程p1再運行進程p2,感受到join()的魅力了吧.現在再試試注釋掉join()看看又會出現什么

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a=time.time()
    p1=Process(target=fun1,args=(4,))
    p2 = Process(target=fun2, args=(6,))
    p1.start()
    p2.start()
    p1.join()
    #p2.join()
    b=time.time()
    print 'finish',b-a

結果:

this is fun1 Mon Jun 05 14:23:57 2017
this is fun2 Mon Jun 05 14:23:58 2017
fun1 finish Mon Jun 05 14:24:01 2017
finish 4.05900001526
fun2 finish Mon Jun 05 14:24:04 2017

Process finished with exit code 0

這次是運行完fun1(因為p1進程有用join(),所以主程序等待p1運行完接著執行下一步),接著繼續運行主進程的print 'finish',最后fun2運行完畢才結束
2.name,daemon,is_alive():

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a=time.time()
    p1=Process(name='fun1進程',target=fun1,args=(4,))
    p2 = Process(name='fun2進程',target=fun2, args=(6,))
    p1.daemon=True
    p2.daemon = True
    p1.start()
    p2.start()
    p1.join()
    print p1,p2
    print '進程1:',p1.is_alive(),'進程2:',p2.is_alive()
    #p2.join()
    b=time.time()
    print 'finish',b-a

結果:

this is fun2 Mon Jun 05 14:43:49 2017
this is fun1 Mon Jun 05 14:43:49 2017
fun1 finish Mon Jun 05 14:43:53 2017
<Process(fun1進程, stopped daemon)> <Process(fun2進程, started daemon)>
進程1: False 進程2: True
finish 4.06500005722

Process finished with exit code 0

可以看到,name是給進程賦予名字, 運行到print '進程1:',p1.is_alive(),'進程2:',p2.is_alive() 這句的時候,p1進程已經結束(返回False),p2進程仍然在運行(返回True),但p2沒有用join(),所以直接接著執行主進程,由于用了daemon=Ture,父進程終止后自動終止,p2進程沒有結束就強行結束整個程序了.

3.run()

run()在Process沒有指定target函數時,默認用run()函數運行程序,

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a = time.time()
    p=Process()
    p.start()
    p.join()
    b = time.time()
    print 'finish', b - a

結果:

finish 0.0840001106262

從結果看出,進程p什么也沒做,為了讓進程正常運行,我們醬紫寫:
目標函數沒有參數:

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def fun1():
    print 'this is fun1',time.ctime()
    time.sleep(2)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a = time.time()
    p=Process()
    p.run=fun1
    p.start()
    p.join()
    b = time.time()
    print 'finish', b - a

結果:

this is fun1 Mon Jun 05 16:34:41 2017
fun1 finish Mon Jun 05 16:34:43 2017
finish 2.11500000954

Process finished with exit code 0

目標函數有參數:

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a = time.time()
    p=Process()
    p.run=fun1(2)
    p.start()
    p.join()
    b = time.time()
    print 'finish', b - a

結果:

this is fun1 Mon Jun 05 16:36:27 2017
fun1 finish Mon Jun 05 16:36:29 2017
Process Process-1:
Traceback (most recent call last):
  File "E:\Anaconda2\lib\multiprocessing\process.py", line 258, in _bootstrap
    self.run()
TypeError: 'NoneType' object is not callable
finish 2.0529999733

Process finished with exit code 0

目標函數有參數的出現了異常,為什么呢?我現在還找不到原因,但是實踐發現,當最后一個參數賦予進程運行后,沒有其他參數,就會出現這個異常,有人知道的望告知.

二.進程池

對于需要使用幾個甚至十幾個進程時,我們使用Process還是比較方便的,但是如果要成百上千個進程,用Process顯然太笨了,multiprocessing提供了Pool類,即現在要講的進程池,能夠將眾多進程放在一起,設置一個運行進程上限,每次只運行設置的進程數,等有進程結束,再添加新的進程
Pool(processes =num):設置運行進程數,當一個進程運行完,會添加新的進程進去
apply_async(函數,(參數)):非阻塞,其中參數是tulpe類型,
apply(函數,(參數)):阻塞
close():關閉pool,不能再添加新的任務
terminate():結束運行的進程,不再處理未完成的任務
join():和Process介紹的作用一樣, 但要在close或terminate之后使用。

1.單個進程池

# -*- coding:utf-8 -*-
from multiprocessing import Pool
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a=time.time()
    pool = Pool(processes =3)  # 可以同時跑3個進程
    for i in range(3,8):
        pool.apply_async(fun1,(i,))
    pool.close()
    pool.join()
    b=time.time()
    print 'finish',b-a

結果:

this is fun1 Mon Jun 05 15:15:38 2017
this is fun1 Mon Jun 05 15:15:38 2017
this is fun1 Mon Jun 05 15:15:38 2017
fun1 finish Mon Jun 05 15:15:41 2017
this is fun1 Mon Jun 05 15:15:41 2017
fun1 finish Mon Jun 05 15:15:42 2017
this is fun1 Mon Jun 05 15:15:42 2017
fun1 finish Mon Jun 05 15:15:43 2017
fun1 finish Mon Jun 05 15:15:47 2017
fun1 finish Mon Jun 05 15:15:49 2017
finish 11.1370000839

Process finished with exit code 0

從上面的結果可以看到,設置了3個運行進程上限,15:15:38這個時間同時開始三個進程,當第一個進程結束時(參數為3秒那個進程),會添加新的進程,如此循環,直至進程池運行完再執行主進程語句b=time.time() print 'finish',b-a .這里用到非阻塞apply_async(),再來對比下阻塞apply()

# -*- coding:utf-8 -*-
from multiprocessing import Pool
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a=time.time()
    pool = Pool(processes =3)  # 可以同時跑3個進程
    for i in range(3,8):
        pool.apply(fun1,(i,))
    pool.close()
    pool.join()
    b=time.time()
    print 'finish',b-a

結果:

this is fun1 Mon Jun 05 15:59:26 2017
fun1 finish Mon Jun 05 15:59:29 2017
this is fun1 Mon Jun 05 15:59:29 2017
fun1 finish Mon Jun 05 15:59:33 2017
this is fun1 Mon Jun 05 15:59:33 2017
fun1 finish Mon Jun 05 15:59:38 2017
this is fun1 Mon Jun 05 15:59:38 2017
fun1 finish Mon Jun 05 15:59:44 2017
this is fun1 Mon Jun 05 15:59:44 2017
fun1 finish Mon Jun 05 15:59:51 2017
finish 25.1610000134

Process finished with exit code 0

可以看到,阻塞是當一個進程結束后,再進行下一個進程,一般我們都用非阻塞apply_async()

2.多個進程池

上面是使用單個進程池的,對于多個進程池,我們可以用for循環,直接看代碼

# -*- coding:utf-8 -*-
from multiprocessing import Pool
import time

def fun1(t):
    print 'this is fun1',time.ctime()
    time.sleep(t)
    print 'fun1 finish',time.ctime()

def fun2(t):
    print 'this is fun2',time.ctime()
    time.sleep(t)
    print 'fun2 finish',time.ctime()

if __name__ == '__main__':
    a=time.time()
    pool = Pool(processes =3)  # 可以同時跑3個進程
    for fun in [fun1,fun2]:
        for i in range(3,8):
            pool.apply_async(fun,(i,))
    pool.close()
    pool.join()
    b=time.time()
    print 'finish',b-a

結果:

this is fun1 Mon Jun 05 16:04:38 2017
this is fun1 Mon Jun 05 16:04:38 2017
this is fun1 Mon Jun 05 16:04:38 2017
fun1 finish Mon Jun 05 16:04:41 2017
this is fun1 Mon Jun 05 16:04:41 2017
fun1 finish Mon Jun 05 16:04:42 2017
this is fun1 Mon Jun 05 16:04:42 2017
fun1 finish Mon Jun 05 16:04:43 2017
this is fun2 Mon Jun 05 16:04:43 2017
fun2 finish Mon Jun 05 16:04:46 2017
this is fun2 Mon Jun 05 16:04:46 2017
fun1 finish Mon Jun 05 16:04:47 2017
this is fun2 Mon Jun 05 16:04:47 2017
fun1 finish Mon Jun 05 16:04:49 2017
this is fun2 Mon Jun 05 16:04:49 2017
fun2 finish Mon Jun 05 16:04:50 2017
this is fun2 Mon Jun 05 16:04:50 2017
fun2 finish Mon Jun 05 16:04:52 2017
fun2 finish Mon Jun 05 16:04:55 2017
fun2 finish Mon Jun 05 16:04:57 2017
finish 19.1670000553

Process finished with exit code 0

看到了,在fun1運行完接著運行fun2.

另外對于沒有參數的情況,就直接 pool.apply_async(funtion),無需寫上參數.

在學習編寫程序過程,曾遇到不用if _name_ == '_main':而直接運行程序,這樣結果會出錯,經查詢,在Windows上要想使用進程模塊,就必須把有關進程的代碼寫在當前.py文件的if _name == ‘_main’ :語句的下面,才能正常使用Windows下的進程模塊。Unix/Linux下則不需要。原因有人這么說:在執行的時候,由于你寫的 py 會被當成module 讀進執行。所以,一定要判斷自身是否為 _main。也就是要:

if __name__ == ‘__main__’ :
# do something.

這里我自己還搞不清楚,期待以后能夠理解

學習的過程中,還涉及了經常和進程一起運用的隊列Queue和線程threading,有時間以后再寫吧

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內容

  • 又來到了一個老生常談的問題,應用層軟件開發的程序員要不要了解和深入學習操作系統呢? 今天就這個問題開始,來談談操...
    tangsl閱讀 4,153評論 0 23
  • 1.進程 1.1多線程的引入 現實生活中 有很多的場景中的事情是同時進行的,比如開車的時候手和腳共同來駕駛汽車,再...
    TENG書閱讀 512評論 0 0
  • 簡介 multiprocessing包是python中用來提供多進程管理的包,能完全利用電腦的多核功能。在Unix...
    aialin閱讀 1,439評論 0 4
  • 1.1.1多任務的引入 什么叫“多任務”呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器...
    PythonMaO閱讀 474評論 0 1
  • 夜籠寒煙沙似畫, 佳影添燈獨對答。 許君莫嘆千里異, 明月識途送牽掛。
    靈魂搖擺閱讀 207評論 0 0