多線程的優(yōu)勢(shì)在于可以同時(shí)運(yùn)行多個(gè)任務(wù)(至少感覺起來(lái)是這樣)。但是當(dāng)線程需要共享數(shù)據(jù)時(shí),可能存在數(shù)據(jù)不同步的問題??紤]這樣一種情況:一個(gè)列表里所有元素都是0,線程"set"從后向前把所有元素改成1,而線程"print"負(fù)責(zé)從前往后讀取列表并打印。那么,可能線程"set"開始改的時(shí)候,線程"print"便來(lái)打印列表了,輸出就成了一半0一半1,這就是數(shù)據(jù)的不同步。為了避免這種情況,引入了鎖的概念。
鎖有兩種狀態(tài)——鎖定和未鎖定。每當(dāng)一個(gè)線程比如"set"要訪問共享數(shù)據(jù)時(shí),必須先獲得鎖定;如果已經(jīng)有別的線程比如"print"獲得鎖定了,那么就讓線程"set"暫停,也就是同步阻塞;等到線程"print"訪問完畢,釋放鎖以后,再讓線程"set"繼續(xù)。經(jīng)過這樣的處理,打印列表時(shí)要么全部輸出0,要么全部輸出1,不會(huì)再出現(xiàn)一半0一半1的尷尬場(chǎng)面。
1.線程通信(條件變量)
然而還有另外一種尷尬的情況:列表并不是一開始就有的;而是通過線程"create"創(chuàng)建的。如果"set"或者"print"在"create"還沒有運(yùn)行的時(shí)候就訪問列表,將會(huì)出現(xiàn)一個(gè)異常。使用鎖可以解決這個(gè)問題,但是"set"和"print"將需要一個(gè)無(wú)限循環(huán)——他們不知道"create"什么時(shí)候會(huì)運(yùn)行,讓"create"在運(yùn)行后通知"set"和"print"顯然是一個(gè)更好的解決方案。于是,引入了條件變量。
條件變量允許線程比如"set"和"print"在條件不滿足的時(shí)候(列表為None時(shí))等待,等到條件滿足的時(shí)候(列表已經(jīng)創(chuàng)建)發(fā)出一個(gè)通知,告訴"set"和"print"條件已經(jīng)有了,你們?cè)撈鸫哺苫盍?然后"set"和"print"才繼續(xù)運(yùn)行。
2.線程運(yùn)行和阻塞的狀態(tài)轉(zhuǎn)換
最后看看線程運(yùn)行和阻塞狀態(tài)的轉(zhuǎn)換。
阻塞有三種情況:
同步阻塞是指處于競(jìng)爭(zhēng)鎖定的狀態(tài),線程請(qǐng)求鎖定時(shí)將進(jìn)入這個(gè)狀態(tài),一旦成功獲得鎖定又恢復(fù)到運(yùn)行狀態(tài);
等待阻塞是指等待其他線程通知的狀態(tài),線程獲得條件鎖定后,調(diào)用“等待”將進(jìn)入這個(gè)狀態(tài),一旦其他線程發(fā)出通知,線程將進(jìn)入同步阻塞狀態(tài),再次競(jìng)爭(zhēng)條件鎖定;
而其他阻塞是指調(diào)用time.sleep()、anotherthread.join()或等待IO時(shí)的阻塞,這個(gè)狀態(tài)下線程不會(huì)釋放已獲得的鎖定。
以上內(nèi)容為大家介紹了python線程同步(鎖),希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。