首頁 > 軟體

Python Thread虛假喚醒概念與防範詳解

2023-02-28 18:01:43

什麼是虛假喚醒

虛假喚醒是一種現象,它只會出現在多執行緒環境中,指的是在多執行緒環境下,多個執行緒等待在同一個條件上,等到條件滿足時,所有等待的執行緒都被喚醒,但由於多個執行緒執行的順序不同,後面競爭到鎖的執行緒在獲得時間片時條件已經不再滿足,執行緒應該繼續睡眠但是卻繼續往下執行的一種現象。

上面是比較書面化的定義,我們用人能聽懂的話來介紹一下虛假喚醒。

多執行緒環境的程式設計中,我們經常遇到讓多個執行緒等待在一個條件上,等到這個條件成立的時候我們再去喚醒這些執行緒,讓它們接著往下執行程式碼的場景。假如某一時刻條件成立,所有的執行緒都被喚醒了,然後去競爭鎖,因為同一時刻只會有一個執行緒能拿到鎖,其他的執行緒都會阻塞到鎖上無法往下執行,等到成功爭搶到鎖的執行緒消費完條件,釋放了鎖,後面的執行緒繼續執行,拿到鎖時這個條件很可能已經不滿足了,這個時候執行緒應該繼續在這個條件上阻塞下去,而不應該繼續執行,如果繼續執行了,就說發生了虛假喚醒。

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()
 

現在改用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()

還沒有出現問題!!!

使用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()

這時就出現了問題!!!

現在改用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判斷,所以每次蕭炎和唐三醒過來之後都會再判斷一下有沒有蘋果(喚醒自己的條件是否滿足),如果不滿足,就會繼續睡下去,不會接著往下執行,從而避免了虛假喚醒。

總結

等待在一個條件上的執行緒被全部喚醒後會去競爭鎖,所以這些執行緒會一個一個地去消費這個條件,等到後面的執行緒去消費這個條件時,條件可能已經不滿足了,所以每個被喚醒的執行緒都需要再檢查一次條件是否滿足。如果不滿足,應該繼續睡下去;只有滿足了才能往下執行。

到此這篇關於Python Thread虛假喚醒概念與防範詳解的文章就介紹到這了,更多相關Python Thread虛假喚醒內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com