Python多進程中的multiprocessing

一眯娱、多任務(wù)的引入

在現(xiàn)實生活中,有很多的場景中的事情是同時進行的爬凑,比如開車的時候徙缴,手和腳共同來駕駛汽車;再比如嘁信,唱歌跳舞也是同時進行的于样。

下來,我們在程序里面潘靖,模擬一下“唱歌跳舞”這件事情穿剖,如下:

from time import sleep

def sing():
    for i in range(3):
        print("正在唱歌...%d"%i)
        sleep(1)

def dance():
    for i in range(3):
        print("正在跳舞...%d"%i)
        sleep(1)

if __name__ == '__main__':
    sing() #唱歌
    dance() #跳舞

注意:

  1. 很顯然剛剛的程序并沒有完成唱歌和跳舞同時進行的要求。
  2. 如果想要實現(xiàn)“唱歌跳舞”同時進行卦溢,那么就需要一個新的方法糊余,叫做:多任務(wù)。

二单寂、多任務(wù)的概念

什么叫“多任務(wù)”呢贬芥?簡單地說,就是操作系統(tǒng)可以同時運行多個任務(wù)宣决。打個比方蘸劈,我們一邊在用瀏覽器上網(wǎng),一邊在聽MP3尊沸,一邊在用Word寫作業(yè)威沫,這就是多任務(wù)贤惯,至少同時有3個任務(wù)正在運行。還有很多任務(wù)悄悄地在后臺同時運行著壹甥,只是桌面上沒有顯示而已救巷。

現(xiàn)在,多核CPU已經(jīng)非常普及了句柠。但是浦译,即使過去的單核CPU,也可以執(zhí)行多任務(wù)溯职。由于CPU執(zhí)行代碼都是順序執(zhí)行的精盅,那么,單核CPU是怎么執(zhí)行多任務(wù)的呢谜酒?

答案就是操作系統(tǒng)輪流讓各個任務(wù)交替執(zhí)行叹俏,任務(wù)1執(zhí)行0.01秒,切換到任務(wù)2僻族,任務(wù)2執(zhí)行0.01秒粘驰,再切換到任務(wù)3,執(zhí)行0.01秒……這樣反復(fù)執(zhí)行下去述么。表面上看蝌数,每個任務(wù)都是交替執(zhí)行的,但是度秘,由于CPU的執(zhí)行速度實在是太快了顶伞,我們感覺就像所有任務(wù)都在同時執(zhí)行一樣。

真正的并行執(zhí)行多任務(wù)只能在多核CPU上實現(xiàn)剑梳,但是唆貌,由于任務(wù)數(shù)量遠遠多于CPU的核心數(shù)量,所以垢乙,操作系統(tǒng)也會自動把很多任務(wù)輪流調(diào)度到每個核心上執(zhí)行锨咙。

實現(xiàn)多任務(wù)的策略(調(diào)度算法):

  1. 時間片輪轉(zhuǎn)
  2. 優(yōu)先級調(diào)度

實現(xiàn)多任務(wù)的三種方式:

  1. 進程
  2. 線程
  3. 協(xié)程

多任務(wù)的原理:

  1. 并發(fā):假的多任務(wù),時間片的輪轉(zhuǎn)追逮,快速的交替運行任務(wù)蓖租。
  2. 并行:真的多任務(wù),一個核處理一個任務(wù)羊壹。

三蓖宦、進程和程序的區(qū)別

編寫完畢的代碼,在沒有運行的時候油猫,稱之為程序稠茂。正在運行著的代碼,就成為進程。

進程睬关,除了包含代碼以外诱担,還有需要運行的環(huán)境等,所以和程序是有區(qū)別的电爹。

四蔫仙、multiprocessing

如果你打算編寫多進程的服務(wù)程序,Unix/Linux無疑是正確的選擇丐箩。由于Windows沒有fork調(diào)用摇邦,難道在Windows上無法用Python編寫多進程的程序?

由于Python是跨平臺的屎勘,自然也應(yīng)該提供一個跨平臺的多進程支持施籍。multiprocessing模塊就是跨平臺版本的多進程模塊。

multiprocessing模塊提供了一個Process類來代表一個進程對象概漱,下面的例子演示了啟動一個子進程并等待其結(jié)束:

from multiprocessing import Process
import os

# 子進程要執(zhí)行的代碼
def run_proc(name):
    print('子進程運行中丑慎,name= %s ,pid=%d...' % (name, os.getpid()))

if __name__ == '__main__':
    print('父進程 %d.' % os.getppid())
    p = Process(target=run_proc, args=('test',))
    print('子進程將要執(zhí)行')
    p.start()
    p.join()
    print('子進程已結(jié)束')

說明:

  1. 創(chuàng)建子進程時,只需要傳入一個執(zhí)行函數(shù)和函數(shù)的參數(shù)瓤摧,創(chuàng)建一個Process實例竿裂,用start()方法啟動,這樣創(chuàng)建進程比fork()還要簡單照弥。
  2. join()方法可以等待子進程結(jié)束后再繼續(xù)往下運行腻异,通常用于進程間的同步。

五产喉、Process語法結(jié)構(gòu)

Process([group [, target [, name [, args [, kwargs]]]]])

  • target:表示這個進程實例所調(diào)用對象捂掰;
  • args:表示調(diào)用對象的位置參數(shù)元組敢会;
  • kwargs:表示調(diào)用對象的關(guān)鍵字參數(shù)字典曾沈;
  • name:為當(dāng)前進程實例的別名;
  • group:大多數(shù)情況下用不到鸥昏;

Process類常用方法:

  • is_alive():判斷進程實例是否還在執(zhí)行塞俱;
  • join([timeout]):是否等待進程實例執(zhí)行結(jié)束,或等待多少秒吏垮;
  • start():啟動進程實例(創(chuàng)建子進程)障涯;
  • run():如果沒有給定target參數(shù),對這個對象調(diào)用start()方法時膳汪,就將執(zhí)行對象中的run()方法唯蝶;
  • terminate():不管任務(wù)是否完成,立即終止遗嗽;

Process類常用屬性:

  • name:當(dāng)前進程實例別名粘我,默認(rèn)為Process-N,N為從1開始遞增的整數(shù);
  • pid:當(dāng)前進程實例的PID值征字;
from multiprocessing import Process
import os
from time import sleep

# 子進程要執(zhí)行的代碼
def run_proc(name, age, **kwargs):
    for i in range(10):
        print('子進程運行中都弹,name= %s,age=%d ,pid=%d...' % (name, age,os.getpid()))
        print(kwargs)
        sleep(0.5)

if __name__ == '__main__':
    print('父進程 %d.' % os.getppid())
    p = Process(target=run_proc, args=('test',18), kwargs={"m":20})
    print('子進程將要執(zhí)行')
    p.start()
    sleep(1)
    p.terminate()
    p.join()
    print('子進程已結(jié)束')

進程編號的作用:

  • 進程的編號的目的是驗證主進程和子進程的關(guān)系,可以得知子進程是由哪個主進程創(chuàng)建出來的匙姜。

獲取進程編號的方式:

  1. 獲取主(父)進程pid:os.getppid()
  2. 獲取子進程pid:os.getpid()
  • 所有的子進程都來自于父進程畅厢,因此一個程序中的主進程編號得知,子進程編號按照主進程編號為起始值加一計算氮昧。
  • multiprocessing.current_process()方法獲取當(dāng)前的當(dāng)前進程的詳細(xì)信息(進程名稱和進程編號)框杜。

multiprocessing.current_process():獲取當(dāng)前的當(dāng)前進程的詳細(xì)信息(進程名稱和進程編號)。

from multiprocessing import Process
import time
import os

#兩個子進程將會調(diào)用的兩個方法
def  worker_1(interval):
    print("worker_1,父進程(%s),當(dāng)前進程(%s)"%(os.getppid(),os.getpid()))
    t_start = time.time()
    time.sleep(interval) #程序?qū)粧炱餴nterval秒
    t_end = time.time()
    print("worker_1,執(zhí)行時間為'%0.2f'秒"%(t_end - t_start))

def  worker_2(interval):
    print("worker_2,父進程(%s),當(dāng)前進程(%s)"%(os.getppid(),os.getpid()))
    t_start = time.time()
    time.sleep(interval)
    t_end = time.time()
    print("worker_2,執(zhí)行時間為'%0.2f'秒"%(t_end - t_start))

#輸出當(dāng)前程序的ID
print("進程ID:%s"%os.getpid())

#創(chuàng)建兩個進程對象郭计,target指向這個進程對象要執(zhí)行的對象名稱霸琴,
#args后面的元組中,是要傳遞給worker_1方法的參數(shù)昭伸,
#因為worker_1方法就一個interval參數(shù)梧乘,這里傳遞一個整數(shù)2給它,
#如果不指定name參數(shù)庐杨,默認(rèn)的進程對象名稱為Process-N选调,N為一個遞增的整數(shù)
p1=Process(target=worker_1,args=(2,))
p2=Process(target=worker_2,name="dongGe",args=(1,))

#使用"進程對象名稱.start()"來創(chuàng)建并執(zhí)行一個子進程,
#這兩個進程對象在start后灵份,就會分別去執(zhí)行worker_1和worker_2方法中的內(nèi)容
p1.start()
p2.start()

#同時父進程仍然往下執(zhí)行仁堪,如果p2進程還在執(zhí)行,將會返回True
print("p2.is_alive=%s"%p2.is_alive())

#輸出p1和p2進程的別名和pid
print("p1.name=%s"%p1.name)
print("p1.pid=%s"%p1.pid)
print("p2.name=%s"%p2.name)
print("p2.pid=%s"%p2.pid)

#join括號中不攜帶參數(shù)填渠,表示父進程在這個位置要等待p1進程執(zhí)行完成后弦聂,
#再繼續(xù)執(zhí)行下面的語句,一般用于進程間的數(shù)據(jù)同步氛什,如果不寫這一句莺葫,
#下面的is_alive判斷將會是True,在shell(cmd)里面調(diào)用這個程序時
#可以完整的看到這個過程枪眉,大家可以嘗試著將下面的這條語句改成p1.join(1)捺檬,
#因為p2需要2秒以上才可能執(zhí)行完成,父進程等待1秒很可能不能讓p1完全執(zhí)行完成,
#所以下面的print會輸出True,即p1仍然在執(zhí)行
p1.join()
print("p1.is_alive=%s"%p1.is_alive())

六瞬痘、進程的創(chuàng)建-Process子類

創(chuàng)建新的進程還能夠使用類的方式,可以自定義一個類烤镐,繼承Process類,每次實例化這個類的時候棍鳖,就等同于實例化一個進程對象炮叶,請看下面的實例:

from multiprocessing import Process
import time
import os

#繼承Process類
class Process_Class(Process):
    #因為Process類本身也有__init__方法,這個子類相當(dāng)于重寫了這個方法,
    #但這樣就會帶來一個問題悴灵,我們并沒有完全的初始化一個Process類扛芽,所以就不能使用從這個類繼承的一些方法和屬性,
    #最好的方法就是將繼承類本身傳遞給Process.__init__方法积瞒,完成這些初始化操作
    def __init__(self,interval):
        Process.__init__(self)
        self.interval = interval

    #重寫了Process類的run()方法
    def run(self):
        print("子進程(%s) 開始執(zhí)行川尖,父進程為(%s)"%(os.getpid(),os.getppid()))
        t_start = time.time()
        time.sleep(self.interval)
        t_stop = time.time()
        print("(%s)執(zhí)行結(jié)束,耗時%0.2f秒"%(os.getpid(),t_stop-t_start))

if __name__=="__main__":
    t_start = time.time()
    print("當(dāng)前程序進程(%s)"%os.getpid())
    p1 = Process_Class(2)
    #對一個不包含target屬性的Process類執(zhí)行start()方法茫孔,就會運行這個類中的run()方法叮喳,所以這里會執(zhí)行p1.run()
    p1.start()
    p1.join()
    t_stop = time.time()
    print("(%s)執(zhí)行結(jié)束,耗時%0.2f"%(os.getpid(),t_stop-t_start))

七缰贝、Process中kill的方法

在python語言中馍悟,我們可以使用os模塊中的kill方法,根據(jù)pid殺死相應(yīng)進程剩晴。

語法:os.kill(進程編號, 信號編號)

八锣咒、進程的特性

進程的特性介紹:

  1. 進程之間不共享全局變量
  2. 主進程會等待所有的子進程結(jié)束之后再結(jié)束

8.1 不共享全局變量

當(dāng)一個進程對全局變量進行數(shù)據(jù)的修改,對于其他進程而言不會造成任何的影響赞弥,可以理解為每個進程都拿的是最初始的全局變量毅整。或者可以理解為全局變量就是所謂資源绽左,當(dāng)創(chuàng)建一個進程悼嫉,則系統(tǒng)會直接給這個進程里面復(fù)制一個全局變量。針對于這個全局變量而言拼窥,在進程之間都是相互獨立存在的戏蔑,之間沒有任何的聯(lián)系。


三個進程分別操作的都是自己進程內(nèi)部的全局變量test_list,不會對其他的進程里面的全局變量造成影響鲁纠,所以進程之間不共享全局變量总棵。他們的關(guān)系只有一點,不同進程之間的全局變量的名字相同而已房交。

8.2 所有子進程結(jié)束主進程才會結(jié)束

主進程會等待所有的子進程執(zhí)行結(jié)束之后才能結(jié)束

在主進程結(jié)束之前彻舰,手動結(jié)束了所有的子進程伐割,那么程序的結(jié)束由主進程的結(jié)束來控制

如果需要實現(xiàn)主進程結(jié)束則整個程序結(jié)束:

  1. 在主進程結(jié)束之前候味,保證所有子進程結(jié)束使用子進程的terminate()
  2. 在子進程開啟之前,設(shè)置當(dāng)前子進程被被主進程守護隔心,子進程的deamon屬性設(shè)為true則意味著這個子進程被主進程守護白群,主進程結(jié)束守護結(jié)束,子進程也結(jié)束

九硬霍、單進程與多線程的優(yōu)劣

9.1 單進程

默認(rèn)程序運行創(chuàng)建一個進程帜慢。

一個Python文件運行,就是開啟一個進程去處理。

進程中的場景:主線程去執(zhí)行代碼粱玲。

9.2 多進程

一個Python文件運行躬柬,占用一個進程去處理,假如同時要運行第二個Python文件抽减,同樣給第二個Python文件開啟一個進程去處理允青。

多進程可以完成多任務(wù),每個進程就好比一個獨立車間卵沉,每個車間都各自在運營颠锉,每個進程也是各自在運行,執(zhí)行各自的任務(wù)史汗。

9.3 優(yōu)劣對比

  1. 單進程開發(fā)簡單琼掠;多線程開發(fā)復(fù)雜;
  2. 單進程在處理高并發(fā)時一般采用多啟動進程的方式停撞;多線程僅需啟動多個線程瓷蛙。進程的切換開銷比線程大;
  3. 多進程之間如果有信息通信則相對多線程效率較低戈毒,因為多線程屬于同一地址空間的訪問速挑,效率相對較高(暫不涉及鎖等一致性策略的影響);
  4. 多進程穩(wěn)定好副硅,一個進程死了不影響其他進程姥宝;多線程中,任意一個出現(xiàn)問題恐疲,將影響到所有腊满。
希望本文對你有所幫助~~如果對接口測試、自動化測試培己、面試經(jīng)驗交流感興趣可以加入我們碳蛋。642830685,免費領(lǐng)取最新軟件測試大廠面試資料和Python自動化省咨、接口肃弟、框架搭建學(xué)習(xí)資料!技術(shù)大牛解惑答疑零蓉,同行一起交流笤受。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市敌蜂,隨后出現(xiàn)的幾起案子箩兽,更是在濱河造成了極大的恐慌,老刑警劉巖章喉,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汗贫,死亡現(xiàn)場離奇詭異身坐,居然都是意外死亡,警方通過查閱死者的電腦和手機落包,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門部蛇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咐蝇,你說我怎么就攤上這事搪花。” “怎么了嘹害?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵撮竿,是天一觀的道長。 經(jīng)常有香客問我笔呀,道長幢踏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任许师,我火速辦了婚禮房蝉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘微渠。我一直安慰自己搭幻,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布逞盆。 她就那樣靜靜地躺著檀蹋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪云芦。 梳的紋絲不亂的頭發(fā)上俯逾,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音舅逸,去河邊找鬼桌肴。 笑死,一個胖子當(dāng)著我的面吹牛琉历,可吹牛的內(nèi)容都是我干的坠七。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼旗笔,長吁一口氣:“原來是場噩夢啊……” “哼彪置!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起换团,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤悉稠,失蹤者是張志新(化名)和其女友劉穎宫蛆,沒想到半個月后艘包,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體的猛,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年想虎,在試婚紗的時候發(fā)現(xiàn)自己被綠了卦尊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡舌厨,死狀恐怖岂却,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情裙椭,我是刑警寧澤躏哩,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站揉燃,受9級特大地震影響扫尺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炊汤,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一正驻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抢腐,春花似錦姑曙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至啼染,卻和暖如春醋界,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背提完。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工形纺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人徒欣。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓逐样,卻偏偏與公主長得像,于是被迫代替她去往敵國和親打肝。 傳聞我的和親對象是個殘疾皇子脂新,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一、概念 在使用multiprocessing庫實現(xiàn)多進程之前粗梭,我們先來了解一下操作系統(tǒng)相關(guān)的知識争便。 Unix/L...
    艾胖胖胖閱讀 38,050評論 1 18
  • 在現(xiàn)實社會,我們經(jīng)常需要一種場景断医,就是同時有多個事情需要執(zhí)行滞乙,如在瀏覽網(wǎng)頁的同時需要聽音樂奏纪。比如說在跳舞的時候要唱...
    伊森H閱讀 814評論 2 2
  • 現(xiàn)在, 多核CPU已經(jīng)非常普及了斩启, 但是序调, 即使過去的單核CPU, 也可以執(zhí)行多任務(wù)兔簇。 CPU執(zhí)行代碼都是順序執(zhí)行...
    LittlePy閱讀 4,795評論 0 3
  • 進程與線程 進程(Process)和線程(Thread)都是操作系統(tǒng)中的基本概念发绢,它們之間有一些優(yōu)劣和差異。 1....
    0893051f5f11閱讀 465評論 1 1
  • 多進程編程 多任務(wù)編程分為:多進程垄琐,多線程边酒。 多任務(wù):多個任務(wù)同時執(zhí)行,提高CPU的利用率狸窘。 進程:...