Python Thread虛假喚醒概念與防范詳解
什么是虛假喚醒
虛假喚醒是一種現(xiàn)象,它只會出現(xiàn)在多線程環(huán)境中,指的是在多線程環(huán)境下,多個(gè)線程等待在同一個(gè)條件上,等到條件滿足時(shí),所有等待的線程都被喚醒,但由于多個(gè)線程執(zhí)行的順序不同,后面競爭到鎖的線程在獲得時(shí)間片時(shí)條件已經(jīng)不再滿足,線程應(yīng)該繼續(xù)睡眠但是卻繼續(xù)往下運(yùn)行的一種現(xiàn)象。
上面是比較書面化的定義,我們用人能聽懂的話來介紹一下虛假喚醒。
多線程環(huán)境的編程中,我們經(jīng)常遇到讓多個(gè)線程等待在一個(gè)條件上,等到這個(gè)條件成立的時(shí)候我們再去喚醒這些線程,讓它們接著往下執(zhí)行代碼的場景。假如某一時(shí)刻條件成立,所有的線程都被喚醒了,然后去競爭鎖,因?yàn)橥粫r(shí)刻只會有一個(gè)線程能拿到鎖,其他的線程都會阻塞到鎖上無法往下執(zhí)行,等到成功爭搶到鎖的線程消費(fèi)完條件,釋放了鎖,后面的線程繼續(xù)運(yùn)行,拿到鎖時(shí)這個(gè)條件很可能已經(jīng)不滿足了,這個(gè)時(shí)候線程應(yīng)該繼續(xù)在這個(gè)條件上阻塞下去,而不應(yīng)該繼續(xù)執(zhí)行,如果繼續(xù)執(zhí)行了,就說發(fā)生了虛假喚醒。
import threading from threading import Condition class Data: def __init__(self, cond, num): self.num = num self.cond = cond def add(self): self.cond: Condition = self.cond self.cond.acquire() if self.num > 0: self.cond.wait() self.num += 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() def decr(self): self.cond: Condition = self.cond self.cond.acquire() if self.num == 0: self.cond.wait() self.num -= 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() if __name__ == '__main__': cond = Condition() num = 0 data = Data(cond, 0) thread_add = threading.Thread(name="A", target=data.add) thread_decr = threading.Thread(name="B", target=data.decr) thread_add.start() thread_decr.start()
現(xiàn)在改用4個(gè)線程
import threading from threading import Condition class Data: def __init__(self, cond, num): self.num = num self.cond = cond def add(self): self.cond: Condition = self.cond self.cond.acquire() if self.num > 0: self.cond.wait() self.num += 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() def decr(self): self.cond: Condition = self.cond self.cond.acquire() if self.num == 0: self.cond.wait() self.num -= 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() if __name__ == '__main__': cond = Condition() num = 0 data = Data(cond, 0) thread_add = threading.Thread(name="A", target=data.add) thread_decr = threading.Thread(name="B", target=data.decr) thread_add2 = threading.Thread(name="C", target=data.add) thread_decr2 = threading.Thread(name="D", target=data.decr) thread_add.start() thread_decr.start() thread_add2.start() thread_decr2.start()
還沒有出現(xiàn)問題?。?!
使用20個(gè)線程同時(shí)跑
import threading from threading import Condition class Data: def __init__(self, cond, num): self.num = num self.cond = cond def add(self): self.cond: Condition = self.cond self.cond.acquire() if self.num > 0: self.cond.wait() self.num += 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() def decr(self): self.cond: Condition = self.cond self.cond.acquire() if self.num == 0: self.cond.wait() self.num -= 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() if __name__ == '__main__': cond = Condition() num = 0 data = Data(cond, 0) for i in range(10): thread_add = threading.Thread(name="A", target=data.add) thread_add.start() for i in range(10): thread_decr = threading.Thread(name="B", target=data.decr) thread_decr.start()
這時(shí)就出現(xiàn)了問題?。?!
現(xiàn)在改用while進(jìn)行判斷
防止虛假喚醒:
import threading from threading import Condition class Data: def __init__(self, cond, num): self.num = num self.cond = cond def add(self): self.cond: Condition = self.cond self.cond.acquire() # 這里采用了while進(jìn)行判斷,防止虛假喚醒 while self.num > 0: self.cond.wait() self.num += 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() def decr(self): self.cond: Condition = self.cond self.cond.acquire() # 這里采用了while進(jìn)行判斷,防止虛假喚醒 while self.num == 0: self.cond.wait() self.num -= 1 print(threading.current_thread().getName(), self.num) self.cond.notifyAll() self.cond.release() if __name__ == '__main__': cond = Condition() num = 0 data = Data(cond, 0) for i in range(10): thread_add = threading.Thread(name="A", target=data.add) thread_add.start() for i in range(10): thread_decr = threading.Thread(name="B", target=data.decr) thread_decr.start()
這個(gè)例子與上面的代碼幾乎沒有差別,只是把if判斷換成了while判斷,所以每次蕭炎和唐三醒過來之后都會再判斷一下有沒有蘋果(喚醒自己的條件是否滿足),如果不滿足,就會繼續(xù)睡下去,不會接著往下運(yùn)行,從而避免了虛假喚醒。
總結(jié)
等待在一個(gè)條件上的線程被全部喚醒后會去競爭鎖,所以這些線程會一個(gè)一個(gè)地去消費(fèi)這個(gè)條件,等到后面的線程去消費(fèi)這個(gè)條件時(shí),條件可能已經(jīng)不滿足了,所以每個(gè)被喚醒的線程都需要再檢查一次條件是否滿足。如果不滿足,應(yīng)該繼續(xù)睡下去;只有滿足了才能往下執(zhí)行。
到此這篇關(guān)于Python Thread虛假喚醒概念與防范詳解的文章就介紹到這了,更多相關(guān)Python Thread虛假喚醒內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Django自關(guān)聯(lián)實(shí)現(xiàn)多級聯(lián)動查詢實(shí)例
這篇文章主要介紹了Django自關(guān)聯(lián)實(shí)現(xiàn)多級聯(lián)動查詢實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05Python中文分詞實(shí)現(xiàn)方法(安裝pymmseg)
這篇文章主要介紹了Python中文分詞實(shí)現(xiàn)方法,通過安裝pymmseg來實(shí)現(xiàn)分詞功能,涉及pymmseg的下載、解壓、安裝及使用技巧,需要的朋友可以參考下2016-06-06利用python-pypcap抓取帶VLAN標(biāo)簽的數(shù)據(jù)包方法
今天小編就為大家分享一篇利用python-pypcap抓取帶VLAN標(biāo)簽的數(shù)據(jù)包方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07python實(shí)現(xiàn)計(jì)算倒數(shù)的方法
這篇文章主要介紹了python實(shí)現(xiàn)計(jì)算倒數(shù)的方法,涉及Python針對數(shù)學(xué)運(yùn)算操作的相關(guān)技巧,需要的朋友可以參考下2015-07-07pandas.read_csv參數(shù)詳解(小結(jié))
這篇文章主要介紹了pandas.read_csv參數(shù)詳解(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Python通過yagmail實(shí)現(xiàn)發(fā)送郵件代碼解析
這篇文章主要介紹了Python通過yagmail實(shí)現(xiàn)發(fā)送郵件代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10利用Pandas讀取某列某行數(shù)據(jù)之loc和iloc用法總結(jié)
loc是location的意思,和iloc中i的意思是指integer,所以它只接受整數(shù)作為參數(shù),下面這篇文章主要給大家介紹了關(guān)于利用Pandas讀取某列某行數(shù)據(jù)之loc和iloc用法的相關(guān)資料,需要的朋友可以參考下2022-03-03Python實(shí)現(xiàn)動態(tài)條形圖繪制的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Python語言實(shí)現(xiàn)動態(tài)條形圖的繪制,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-08-08