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è)置。


??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)注明出處。