Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

python中的wait和notify

這次講一下python中的wait和notify

現在假如有如下情況:

小明:小紅

小紅:在

小明:我喜歡你 小紅:對不起,你是個好人。

對於這種一問一答的方式,我們是否也可以通過加鎖來解決呢,我們看代碼。

import threading

class XiaoMing(threading.Thread):
    def __init__(self,lock):
        super().__init__(name='小明')
    
    
    def run(self):
        lock.acquire()
        print('{}:小紅'.format(self.name))
        lock.release()

        lock.acquire()
        print('{}:我喜歡你'.format(self.name))
        lock.release()

class XiaoHong(threading.Thread):
    def __init__(self,lock):
        super().__init__(name='小紅')
    
    def run(self):
        lock.acquire()
        print('{}:在'.format(self.name))
        lock.release()

        lock.acquire()
        print('{}:對不起,你是個好人'.format(self.name))
        lock.release()

if __name__ == '__main__':
    lock = threading.Lock()
    xiaoming = XiaoMing(lock)
    xiaohong = XiaoHong(lock)
    
    xiaoming.start()
    xiaohong.start()

這個時候會出現一個現象,我們發現執行的結果是

小明:小紅 小明:我喜歡你 小紅:在 小紅:對不起,你是個好人

很顯然,這跟我們想要的結果是不一樣的,那麼為什麼會導致這樣的結果呢。

原因就在於我們在小明說完小紅的時候會釋放鎖,接著小明這個線程又拿到了鎖,這個時候又繼續說了我喜歡你。這就是導致結果跟預期不一致的原因。

因此我們在這裡引出了wait和notify這兩個方法,這兩個方法屬於threading的Condition類,condition是一個條件變量,是用來控制復雜的線程之間的同步。

如果看過condition的源碼,就會發現condition實現了__enter__ 和__exit__這兩個魔術方法,因此我們可以通過with語句來使用condition這個變量。

再說一下wait和notify,wait()只有在被notify喚醒時,才會繼續往下執行。因此會有下面這樣的代碼。

import threading
from threading import Condition


class XiaoMing(threading.Thread):
    def __init__(self,condition):
        super().__init__(name='小明')
        self.condition = condition
    
    def run(self):
        with self.condition:
            print('{}:小紅'.format(self.name))
            self.condition.notify()
            self.condition.wait()

            print('{}:我喜歡你'.format(self.name))
            self.condition.notify()
            self.condition.wait()


class XiaoHong(threading.Thread):
    def __init__(self,condition):
        super().__init__(name='小紅')
        self.condition = condition

    def run(self):
        with self.condition:
            self.condition.wait()
            print('{}:在'.format(self.name))
            self.condition.notify()

            self.condition.wait()
            print('{}:對不起,你是個好人'.format(self.name))
            self.condition.notify()

if __name__ == '__main__':
    condition = threading.Condition()
    xiaoming = XiaoMing(condition)
    xiaohong = XiaoHong(condition)
    
    xiaohong.start()
    xiaoming.start()

運行上面的代碼,我們發現執行結果按照我們預期的進行了。需要注意的一點就是start的順序改了,是小紅先start,小明才start。

如果是小明先start的話,那麼小紅就會在小明notify之後才start,這樣小紅的wait就收不到小明發過來的信號了,因此會導致

程序一直卡住。

其實這個condition的源碼裡面,在初始化condition的時候,就會上一把鎖,這樣另一個線程就進不去with裡面了,

而在調用wait的時候,會先把condition時初始化的鎖釋放掉,然後再分配一把鎖到condition的等待隊列中,等待notify的喚醒。