源碼解讀Python中Event事件的使用
之前我們介紹了鎖、信號量以及隊列,那么本次來說一說事件(Event),它負責多任務之間的同步。
Event 對象內部維護了一個標志,初始時為 False,如果調用 event.set(),可以將它設置為 True, 調用 event.clear() 可以重置為 False。
然后 Event 對象還有一個 wait() 方法,如果內部的標志為 False,那么調用該方法會阻塞。而當標志被設置為 True(通過 set 方法)時,所有任務會解除阻塞并繼續(xù)執(zhí)行。
因此 Event 對象(事件)常用于多任務之間的協(xié)調和同步,例如一個任務在等待某個事件發(fā)生,而另一個任務在發(fā)生時將標志設置為 True,以此來通知正在等待的任務。
下面來實際看一下 Event 對象,另外 Event 有協(xié)程 Event 和線程 Event,我們分別介紹。
協(xié)程 Event
協(xié)程 Event 由 asyncio 模塊提供。
import asyncio
from asyncio import Event
async def task(event: Event):
# 如果 event 內部的標志位是 False,會陷入阻塞
print(f"陷入阻塞,因為標志位 = {event.is_set()}")
await event.wait()
print(f"解除阻塞,因為標志位 = {event.is_set()}")
async def main():
event = Event()
# 任務開始執(zhí)行
asyncio.create_task(task(event))
await asyncio.sleep(3)
# task 內部的 event.wait() 會陷入阻塞
# 3 秒將標志設置為 True
print("將 event 內部的標志位設置為 True")
event.set()
asyncio.run(main())
"""
陷入阻塞,因為標志位 = False
將 event 內部的標志位設置為 True
解除阻塞,因為標志位 = True
"""非常簡單,當調用 event.wait() 時,如果標志是 True,那么相當于綠燈,直接通過;如果標志是 False,那么相當于紅燈,需要等待。
默認情況下是紅燈,通過 event.set() 可以設置為綠燈,也可以通過 event.clear() 重置為紅燈。調用 is_set() 方法可以判斷當前是紅燈還是綠燈,True 為綠燈,False 為紅燈。
然后再來看看它的源碼實現:

當標志位是 False,協(xié)程調用 wait 方法會陷入阻塞,那么阻塞要如何實現呢?沒錯,還是要通過 Future 對象。所以 Future 對象和 asyncio 的實現緊密相關,協(xié)程里面的阻塞等待都是基于 Future 實現的。
而 _waiters 負責保存協(xié)程內部創(chuàng)建的 Future 對象,_value 則表示標志位。至于 _loop 則用于指定事件循環(huán),這個參數已經廢棄了。

is_set() 方法用于查看標志位,clear() 方法用于將標志位設置為 False,比較簡單。
然后是 wait() 方法,如果調用時發(fā)現標志位是 True,那么說明是綠燈,直接通過。否則說明是紅燈,于是創(chuàng)建一個 Future 對象,并添加到 _waiters 中,然后 await 它,從而陷入阻塞。
最后是 set() 方法,如果標志位是 False,將其設置為 True。然后將 _waiters 里面的 future 依次彈出,設置結果集,讓 await fut 的協(xié)程解除阻塞。
整個過程沒有任何難度,非常簡單。
線程 Event
線程 Event 也很簡單,它是由 threading 模塊提供的。
import time
import threading
from threading import Event
def task():
# 如果 event 內部的標志位是 False,會陷入阻塞
print(f"陷入阻塞,因為標志位 = {event.is_set()}")
event.wait()
print(f"解除阻塞,因為標志位 = {event.is_set()}")
def main():
time.sleep(3)
print("將 event 內部的標志位設置為 True")
event.set()
event = Event()
t1 = threading.Thread(target=task)
t2 = threading.Thread(target=main)
t1.start()
t2.start()
"""
陷入阻塞,因為標志位 = False
將 event 內部的標志位設置為 True
解除阻塞,因為標志位 = True
"""用法和協(xié)程 Event 幾乎沒什么區(qū)別,然后看一下它的內部實現。
class Event:
def __init__(self):
# 條件對象,所以事件對象其實是基于條件對象的一個封裝
self._cond = Condition(Lock())
# 標志位,初始為 False
self._flag = False
def is_set(self):
# 標志位是否被設置
return self._flag
isSet = is_set
def set(self):
# 修改共享變量時需要加鎖保護
with self._cond:
# 設置標志位,并喚醒所有阻塞線程
self._flag = True
self._cond.notify_all()
def clear(self):
# 將標志位設置為 False
with self._cond:
self._flag = False
def wait(self, timeout=None):
# 阻塞等待,但支持超時時間
with self._cond:
signaled = self._flag
if not signaled:
signaled = self._cond.wait(timeout)
return signaled非常簡單,Event 內部是基于 Condition 實現的,關于條件變量,我們后續(xù)再詳細介紹。
小結
以上我們就聊了聊 Event 的實現原理,因為操作系統(tǒng)感知不到協(xié)程,所以協(xié)程 Event 基于 Future 對象實現。而線程 Event 則基于條件變量,關于條件變量,我們以后再詳細展開。
到此這篇關于源碼解讀Python中Event事件的使用的文章就介紹到這了,更多相關Python Event內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
對Python 窗體(tkinter)文本編輯器(Text)詳解
今天小編就為大家分享一篇對Python 窗體(tkinter)文本編輯器(Text)詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10
Python編程pytorch深度卷積神經網絡AlexNet詳解
AlexNet和LeNet的架構非常相似。這里我們提供了一個稍微精簡版本的AlexNet,去除了當年需要兩個小型GPU同時運算的設計特點2021-10-10

