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

1.1 線程

? ??? ? 線程是一個(gè)基本的 CPU 執(zhí)行單元,它必須依托于進(jìn)程存活。一個(gè)線程是一個(gè)execution context(執(zhí)行上下文),即一個(gè) CPU 執(zhí)行時(shí)所需要的一串指令。

1.2 進(jìn)程

????????進(jìn)程是指一個(gè)程序在給定數(shù)據(jù)集合上的一次執(zhí)行過程,是系統(tǒng)進(jìn)行資源分配和運(yùn)行調(diào)用的獨(dú)立單位??梢院唵蔚乩斫鉃椴僮飨到y(tǒng)中正在執(zhí)行的程序。也就說,每個(gè)應(yīng)用程序都有一個(gè)自己的進(jìn)程。

????????每一個(gè)進(jìn)程啟動(dòng)時(shí)都會(huì)最先產(chǎn)生一個(gè)線程,即主線程。然后主線程會(huì)再創(chuàng)建其他的子線程。

1.3 兩者的區(qū)別

? ? ? ? (1).線程必須在某個(gè)進(jìn)行中執(zhí)行。

? ? ? ? (2).一個(gè)進(jìn)程可包含多個(gè)線程,其中有且只有一個(gè)主線程。

? ? ? ? (3).多線程共享同個(gè)地址空間、打開的文件以及其他資源。

? ? ? ? (4).多進(jìn)程共享物理內(nèi)存、磁盤、打印機(jī)以及其他資源。

1.4 線程的類型

????????線程的因作用可以劃分為不同的類型。大致可分為:

? ? ? ? (1)主線程

? ? ? ? (2)子線程

????????(3)守護(hù)線程(后臺(tái)線程)

????????(4)前臺(tái)線程


2.3 線程合并

????????Join函數(shù)執(zhí)行順序是逐個(gè)執(zhí)行每個(gè)線程,執(zhí)行完畢后繼續(xù)往下執(zhí)行。主線程結(jié)束后,子線程還在運(yùn)行,join函數(shù)使得主線程等到子線程結(jié)束時(shí)才退出

????????# 將 t1 和 t2 加入到主線程中

????????t1.join()? ? t2.join()

2.4 線程同步與互斥鎖

????????線程之間數(shù)據(jù)共享的。當(dāng)多個(gè)線程對(duì)某一個(gè)共享數(shù)據(jù)進(jìn)行操作時(shí),就需要考慮到線程安全問題。threading模塊中定義了Lock 類,提供了互斥鎖的功能來保證多線程情況下數(shù)據(jù)的正確性。用法的基本步驟:

????????#創(chuàng)建鎖

????????mutex = threading.Lock()

????????#鎖定

????????mutex.acquire([timeout])

????????#釋放

????????mutex.release()

2.6 守護(hù)線程

????????如果希望主線程執(zhí)行完畢之后,不管子線程是否執(zhí)行完畢都隨著主線程一起結(jié)束。我們可以使用setDaemon(bool)函數(shù),它跟join函數(shù)是相反的。它的作用是設(shè)置子線程是否隨主線程一起結(jié)束,必須在start()之前調(diào)用,默認(rèn)為False。




python多線程

????????Thread類為線程的抽象類,其構(gòu)造方法的參數(shù)target指向一個(gè)函數(shù)對(duì)象,即該線程的具體操作。此外還可以有args=來給target函數(shù)傳參數(shù)。需要注意的是當(dāng)傳任何一個(gè)序列進(jìn)去的話Thread會(huì)自動(dòng)把它分解成單個(gè)單個(gè)的元素然后分解傳給target函數(shù)。


????????通過Thread.Lock類來創(chuàng)建簡單的線程鎖。lock = threading.Lock()即可。在某線程start之前,讓lock.acquire(),且lock在acquire()之后不能再acquire,否則會(huì)報(bào)錯(cuò)。當(dāng)線程結(jié)束后調(diào)用lock.release()來釋放鎖就好了。一般而言,有鎖的多線程場景可以提升一部分效率,但在寫文件等時(shí)機(jī)下會(huì)有阻塞等待的情況。相比之下,無鎖多線程場景可以進(jìn)一步提升效率,但是可能會(huì)引起讀寫沖突等問題,所以要慎用。一定要確認(rèn)各個(gè)線程間沒有共同的資源之類的問題后再實(shí)行無鎖多線程。


????????線程的阻塞和掛起,線程的這兩個(gè)狀態(tài)乍一看都是線程暫停不再繼續(xù)往前運(yùn)行,但是引起的原因不太一樣。阻塞是指線程間互相的制約,當(dāng)一個(gè)線程獲得了鎖,其他的線程就被阻塞了,而掛起是出于統(tǒng)一調(diào)度的考慮。換句話說,掛起是一種主動(dòng)的行為,在程序中我們主動(dòng)掛起某個(gè)線程然后可以主動(dòng)放下讓線程繼續(xù)運(yùn)行;而阻塞更多時(shí)候是被動(dòng)發(fā)生的,當(dāng)有線程操作沖突了那么必然是有一方要被阻塞的。從層級(jí)上看,掛起操作是高于阻塞的,也就說一個(gè)線程可以在阻塞的時(shí)候被掛起,然后被喚醒后依然是阻塞狀態(tài)。如果在掛起過程中具備了運(yùn)行條件(即不再阻塞),線程也不會(huì)往前運(yùn)行。

? ??????Condition類的一些方法,首先是acquire和release,Condition內(nèi)部也維護(hù)了一把鎖,默認(rèn)是RLock類,所有關(guān)聯(lián)了同一個(gè)Condition對(duì)象的線程也都會(huì)遵守這把鎖規(guī)定的來進(jìn)行運(yùn)行。

  Condition.wait([timeout])  這個(gè)方法一定要在獲取鎖定之后調(diào)用,調(diào)用這個(gè)方法的Condition對(duì)象所在的線程會(huì)被掛起并且釋放這個(gè)線程獲得的所有鎖,直到接到通知被喚醒或者超時(shí)(如果設(shè)置了Timeout的話),當(dāng)被喚醒之后線程將重新獲取鎖定。

  Condition.notify()  notify就是上面所說的通知,調(diào)用這個(gè)方法之后會(huì)喚醒一個(gè)被掛起的線程。線程的選擇尚不明確,似乎是隨機(jī)的。需要注意的是notify方法只進(jìn)行掛起的喚醒而不涉及鎖的釋放。

  Condition.notify_all()  喚醒所有掛起的線程


????????python線程的事件(event)用于主線程控制其他線程的執(zhí)行,事件主要提供了三個(gè)方法wait、clear、set

????????事件處理的機(jī)制:全局定義了一個(gè)“Flag”,如果“Flag”值為 False,那么當(dāng)程序執(zhí)行 event.wait 方法時(shí)就會(huì)阻塞,如果“Flag”值為True,那么event.wait 方法時(shí)便不再阻塞。

????????clear:將“Flag”設(shè)置為False

????????set:將“Flag”設(shè)置為True

????????用 threading.Event 實(shí)現(xiàn)線程間通信

????????使用threading.Event可以使一個(gè)線程等待其他線程的通知,我們把這個(gè)Event傳遞到線程對(duì)象中,Event默認(rèn)內(nèi)置了一個(gè)標(biāo)志,初始值為False。

????????一旦該線程通過wait()方法進(jìn)入等待狀態(tài),直到另一個(gè)線程調(diào)用該Event的set()方法將內(nèi)置標(biāo)志設(shè)置為True時(shí),該Event會(huì)通知所有等待狀態(tài)的線程恢復(fù)運(yùn)行。


3.1 創(chuàng)建多進(jìn)程

????????python中的多線程其實(shí)并不是真正的多線程,如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多進(jìn)程。multiprocessing支持子進(jìn)程、通信和共享數(shù)據(jù)、執(zhí)行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。


? ? ? ? (1).Process

? ??????創(chuàng)建進(jìn)程的類:Process([group [, target [, name [, args [, kwargs]]]]]),target表示調(diào)用對(duì)象,args表示調(diào)用對(duì)象的位置參數(shù)元組。kwargs表示調(diào)用對(duì)象的字典。name為別名。group實(shí)質(zhì)上不使用。

? ??????方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()啟動(dòng)某個(gè)進(jìn)程。

? ??????屬性:authkey、daemon(要通過start()設(shè)置)、exitcode(進(jìn)程在運(yùn)行時(shí)為None、如果為–N,表示被信號(hào)N結(jié)束)、name、pid。其中daemon是父進(jìn)程終止后自動(dòng)終止,且自己不能產(chǎn)生新進(jìn)程,必須在start()之前設(shè)置。

例1.1:創(chuàng)建函數(shù)并將其作為單個(gè)進(jìn)程


例1.2:創(chuàng)建函數(shù)并將其作為多個(gè)進(jìn)程



??Python 要進(jìn)行多進(jìn)程操作,需要用到muiltprocessing庫,其中的Process類跟threading模塊的Thread類很相似。所以直接看代碼熟悉多進(jìn)程。

3.2 多進(jìn)程通信

進(jìn)程之間不共享數(shù)據(jù)的。如果進(jìn)程之間需要進(jìn)行通信,則要用到Queue模塊或者Pipi模塊來實(shí)現(xiàn)。

Queue

Queue 是多進(jìn)程安全的隊(duì)列,可以實(shí)現(xiàn)多進(jìn)程之間的數(shù)據(jù)傳遞。它主要有兩個(gè)函數(shù),put和get。

(1).put() 用以插入數(shù)據(jù)到隊(duì)列中,put 還有兩個(gè)可選參數(shù):blocked 和 timeout。如果 blocked 為 True(默認(rèn)值),并且 timeout 為正值,該方法會(huì)阻塞 timeout 指定的時(shí)間,直到該隊(duì)列有剩余的空間。如果超時(shí),會(huì)拋出 Queue.Full 異常。如果 blocked 為 False,但該 Queue 已滿,會(huì)立即拋出 Queue.Full 異常。

(2).get()可以從隊(duì)列讀取并且刪除一個(gè)元素。同樣,get 有兩個(gè)可選參數(shù):blocked 和 timeout。如果 blocked 為 True(默認(rèn)值),并且 timeout 為正值,那么在等待時(shí)間內(nèi)沒有取到任何元素,會(huì)拋出 Queue.Empty 異常。如果blocked 為 False,有兩種情況存在,如果 Queue 有一個(gè)值可用,則立即返回該值,否則,如果隊(duì)列為空,則立即拋出 Queue.Empty 異常。

from multiprocessing importProcess, Queue

def put (queue):

? ? queue.put('Queue 用法')

if__name__ =='__main__':? ??

? ? ? ?queue = Queue()? ??

? ? ? ?pro = Process(target=put, args=(queue,))? ??

? ? ? ?pro.start()? ??

? ? ? ?print(queue.get())? ? ?

? ? ? ?pro.join()

作者:猴哥Yuri

鏈接:http://www.itdecent.cn/p/a69dec87e646

來源:簡書

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言:為什么有人說 Python 的多線程是雞肋,不是真正意義上的多線程? 看到這里,也許你會(huì)疑惑。這很正常,所以...
    猴哥愛讀書閱讀 51,926評(píng)論 6 69
  • 進(jìn)程與線程的區(qū)別 現(xiàn)在,多核CPU已經(jīng)非常普及了,但是,即使過去的單核CPU,也可以執(zhí)行多任務(wù)。由于CPU執(zhí)行代碼...
    蘇糊閱讀 843評(píng)論 0 2
  • 線程 引言&動(dòng)機(jī) 考慮一下這個(gè)場景,我們有10000條數(shù)據(jù)需要處理,處理每條數(shù)據(jù)需要花費(fèi)1秒,但讀取數(shù)據(jù)只需要0....
    不浪漫的浪漫_ea03閱讀 416評(píng)論 0 0
  • 最近都在做暑假工,下班時(shí)間除開刷微博,練琴練字,其實(shí)沒有多少時(shí)間看書了,上班看書也不爽,時(shí)不時(shí)有人來,看書像是在做...
    荔枝森林閱讀 271評(píng)論 0 0
  • 整理一下,明天回校。
    假裝沒想到閱讀 247評(píng)論 0 1

友情鏈接更多精彩內(nèi)容