Python多線程與多進(jìn)程

概念理解

對于操作系統(tǒng)來說,一個任務(wù)就是一個進(jìn)程。例如打開瀏覽器,打開word,打開記事本等等,都是獨(dú)立的任務(wù),它們各自為一個或者多個進(jìn)程。這里要注意的是,同一種任務(wù)打開多個,分別屬于不同進(jìn)程,例如chrome打開多個標(biāo)簽,實(shí)際上它創(chuàng)建了多個進(jìn)程。對于一個任務(wù)來說,它有很多子任務(wù),例如播放器,既要解碼視頻、也要解碼音頻,所以在進(jìn)程下存在多線程。在一個進(jìn)程下一定存在一個線程,可以稱它為主線程。操作系統(tǒng)創(chuàng)建進(jìn)程時,會單獨(dú)為每一個進(jìn)程分配各自的資源,進(jìn)程與進(jìn)程之間相互隔離。而進(jìn)程內(nèi)的線程,則共享了當(dāng)前進(jìn)程內(nèi)的資源。可見,操作系統(tǒng)執(zhí)行的粒度是線程,分配資源的粒度是進(jìn)程,我們的多任務(wù)操作系統(tǒng),在單核CPU上是在各個線程上不斷切換而達(dá)到目的,而在多核CPU上則能同時執(zhí)行多個線程任務(wù)。Python能很方便地支持多進(jìn)程、多線程編程,接下來就簡單記錄下,最后再記錄下兩者優(yōu)缺點(diǎn)。

多進(jìn)程

在Linux系統(tǒng)下,有一個非常特殊的函數(shù),fork()。它調(diào)用一次,返回兩次,操作系統(tǒng)自動把當(dāng)前進(jìn)程(父進(jìn)程)復(fù)制了一份(子進(jìn)程),然后分別在父進(jìn)程和子進(jìn)程內(nèi)返回。子進(jìn)程永遠(yuǎn)返回0,父進(jìn)程返回子進(jìn)程的ID。經(jīng)過這樣做,父進(jìn)程就能fork出很多子進(jìn)程,并可以記錄下子進(jìn)程的ID號了,子進(jìn)程可以通過getppid()來獲取父進(jìn)程ID。fork()僅在Unix/Linux下使用,windows則不行。所以,在Python中,存在一個跨平臺的包mutiprocessing,通過引入包中的Process類,就可以創(chuàng)建多進(jìn)程程序了,可以創(chuàng)建一個進(jìn)程p=Process(target=func,args=(*,)),然后利用p.start()及p.join()來執(zhí)行了。以上的join()方法可以等待子進(jìn)程結(jié)束后才往下執(zhí)行,通常用于進(jìn)程間同步。另外,可以用進(jìn)程池的方式,例如p=Pool(n),然后p.apply_async(func,args),這里可以使用n種不同的參數(shù)傳入,建立不同的進(jìn)程。用這種方式時,在調(diào)用join()方法前,要先調(diào)用close()方法,使得不能再添加新進(jìn)程。mutiprocessing包里提供了Queue、Pipe等多種進(jìn)程間通信的方法。可以直接引入Queue類,然后實(shí)例化一個對象。則不同的進(jìn)程可以使用put方法發(fā)信息,同時可以使用get方法取信息。

多線程

多個任務(wù)可以創(chuàng)建多個進(jìn)程來完成,同時也可以創(chuàng)建多個線程來完成,線程是操作系統(tǒng)直接的執(zhí)行單元。Python含有threading這個高級模塊,要啟動一個線程,就是把一個函數(shù)傳出并創(chuàng)建Thread實(shí)例,然后調(diào)用start()方法開始執(zhí)行,例如t=threading.Thread(target=func,name=*),注意這里的name屬性,它是給線程命名的,缺省值為Thread-1···。要注意的是,剛才說了,任何一個進(jìn)程都含有一個線程,而這個主線程則執(zhí)行著我們編寫的程序,可以調(diào)用threading.current_thread().name來查看它,它的名字就叫MainThread。在多線程編程中,有一個最大的問題就在于進(jìn)程內(nèi)的資源被各個線程所共享,進(jìn)程內(nèi)任何變量都可以被任何一個線程修改,因此,線程之間若去修改同一個變量,則可能導(dǎo)致程序Bug。所以,引入了鎖機(jī)制。當(dāng)某個線程去修改某個變量時,可以在變量所在的方法內(nèi)加一把鎖,使得其他線程不能同時執(zhí)行該方法,只有釋放了鎖后,其他線程才能去獲得鎖并獲得修改權(quán)。創(chuàng)建一個鎖是通過lock=threading.Lock()來實(shí)現(xiàn)的,可以使用try···finally···語句,在try之前使用lock.acquire()獲得鎖,然后在try語句里面修改變量,然后在finally語句里加lock.release()來保證鎖一定被釋放,避免成為一個死鎖。

區(qū)別于聯(lián)系

多進(jìn)程的優(yōu)點(diǎn)是穩(wěn)定性好,一個子進(jìn)程崩潰了,不會影響主進(jìn)程以及其余進(jìn)程。但是缺點(diǎn)是創(chuàng)建進(jìn)程的代價非常大,因?yàn)椴僮飨到y(tǒng)要給每個進(jìn)程分配固定的資源,并且,操作系統(tǒng)對進(jìn)程的總數(shù)會有一定的限制,若進(jìn)程過多,操作系統(tǒng)調(diào)度都會存在問題,會造成假死狀態(tài)。多線程優(yōu)點(diǎn)是效率較高一些,但是致命的缺點(diǎn)是任何一個線程崩潰都可能造成整個進(jìn)程的崩潰,因?yàn)樗鼈児蚕砹诉M(jìn)程的內(nèi)存資源池。對于任務(wù)數(shù)來說,無論是多進(jìn)程或者多線程,都不能太多。因?yàn)椴僮飨到y(tǒng)在切換任務(wù)時,會有一系列的保護(hù)現(xiàn)場措施,這要花費(fèi)相當(dāng)?shù)南到y(tǒng)資源,若任務(wù)過多,則大部分資源都被用做干這些了,結(jié)果就是所有任務(wù)都做不好,所以操作系統(tǒng)會限制進(jìn)程的數(shù)量。另外,考慮計算密集型及IO密集型應(yīng)用程序。對于計算密集型,多任務(wù)勢必造成資源浪費(fèi)。對于IO密集型,因?yàn)镮O速度遠(yuǎn)低于CPU計算速度,所以使用多任務(wù)方式可以大大增大程序運(yùn)行效率。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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