Python Thread虛假喚醒概念與防范詳解
什么是虛假喚醒
虛假喚醒是一種現(xiàn)象,它只會出現(xiàn)在多線程環(huán)境中,指的是在多線程環(huán)境下,多個線程等待在同一個條件上,等到條件滿足時,所有等待的線程都被喚醒,但由于多個線程執(zhí)行的順序不同,后面競爭到鎖的線程在獲得時間片時條件已經(jīng)不再滿足,線程應該繼續(xù)睡眠但是卻繼續(xù)往下運行的一種現(xiàn)象。
上面是比較書面化的定義,我們用人能聽懂的話來介紹一下虛假喚醒。
多線程環(huán)境的編程中,我們經(jīng)常遇到讓多個線程等待在一個條件上,等到這個條件成立的時候我們再去喚醒這些線程,讓它們接著往下執(zhí)行代碼的場景。假如某一時刻條件成立,所有的線程都被喚醒了,然后去競爭鎖,因為同一時刻只會有一個線程能拿到鎖,其他的線程都會阻塞到鎖上無法往下執(zhí)行,等到成功爭搶到鎖的線程消費完條件,釋放了鎖,后面的線程繼續(xù)運行,拿到鎖時這個條件很可能已經(jīng)不滿足了,這個時候線程應該繼續(xù)在這個條件上阻塞下去,而不應該繼續(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個線程
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個線程同時跑
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()
這時就出現(xiàn)了問題!?。?/p>
現(xiàn)在改用while進行判斷
防止虛假喚醒:
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進行判斷,防止虛假喚醒
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進行判斷,防止虛假喚醒
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()
這個例子與上面的代碼幾乎沒有差別,只是把if判斷換成了while判斷,所以每次蕭炎和唐三醒過來之后都會再判斷一下有沒有蘋果(喚醒自己的條件是否滿足),如果不滿足,就會繼續(xù)睡下去,不會接著往下運行,從而避免了虛假喚醒。
總結
等待在一個條件上的線程被全部喚醒后會去競爭鎖,所以這些線程會一個一個地去消費這個條件,等到后面的線程去消費這個條件時,條件可能已經(jīng)不滿足了,所以每個被喚醒的線程都需要再檢查一次條件是否滿足。如果不滿足,應該繼續(xù)睡下去;只有滿足了才能往下執(zhí)行。
到此這篇關于Python Thread虛假喚醒概念與防范詳解的文章就介紹到這了,更多相關Python Thread虛假喚醒內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python實現(xiàn)代碼行數(shù)統(tǒng)計示例分享
這篇文章主要介紹了python實現(xiàn)代碼行數(shù)統(tǒng)計的示例,需要的朋友可以參考下2014-02-02
Python采集熱搜數(shù)據(jù)實現(xiàn)詳解
這篇文章主要為大家介紹了Python采集熱搜數(shù)據(jù)實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
tensorflow入門:TFRecordDataset變長數(shù)據(jù)的batch讀取詳解
今天小編就為大家分享一篇tensorflow入門:TFRecordDataset變長數(shù)據(jù)的batch讀取詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
Python?網(wǎng)易易盾滑塊驗證功能的實現(xiàn)
這篇文章主要介紹了Python?網(wǎng)易易盾滑塊驗證,主要是借助之前寫阿里云盾滑塊和極驗滑塊的經(jīng)驗寫的本文,通過使用selenium請求url,并觸發(fā)滑塊驗證,需要的朋友可以參考下2022-05-05
詳解pyqt5的UI中嵌入matplotlib圖形并實時刷新(挖坑和填坑)
這篇文章主要介紹了詳解pyqt5的UI中嵌入matplotlib圖形并實時刷新(挖坑和填坑),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08

