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

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

is_set() 方法用于查看標(biāo)志位,clear() 方法用于將標(biāo)志位設(shè)置為 False,比較簡(jiǎn)單。
然后是 wait() 方法,如果調(diào)用時(shí)發(fā)現(xiàn)標(biāo)志位是 True,那么說明是綠燈,直接通過。否則說明是紅燈,于是創(chuàng)建一個(gè) Future 對(duì)象,并添加到 _waiters 中,然后 await 它,從而陷入阻塞。
最后是 set() 方法,如果標(biāo)志位是 False,將其設(shè)置為 True。然后將 _waiters 里面的 future 依次彈出,設(shè)置結(jié)果集,讓 await fut 的協(xié)程解除阻塞。
整個(gè)過程沒有任何難度,非常簡(jiǎn)單。
線程 Event
線程 Event 也很簡(jiǎn)單,它是由 threading 模塊提供的。
import time
import threading
from threading import Event
def task():
# 如果 event 內(nèi)部的標(biāo)志位是 False,會(huì)陷入阻塞
print(f"陷入阻塞,因?yàn)闃?biāo)志位 = {event.is_set()}")
event.wait()
print(f"解除阻塞,因?yàn)闃?biāo)志位 = {event.is_set()}")
def main():
time.sleep(3)
print("將 event 內(nèi)部的標(biāo)志位設(shè)置為 True")
event.set()
event = Event()
t1 = threading.Thread(target=task)
t2 = threading.Thread(target=main)
t1.start()
t2.start()
"""
陷入阻塞,因?yàn)闃?biāo)志位 = False
將 event 內(nèi)部的標(biāo)志位設(shè)置為 True
解除阻塞,因?yàn)闃?biāo)志位 = True
"""用法和協(xié)程 Event 幾乎沒什么區(qū)別,然后看一下它的內(nèi)部實(shí)現(xiàn)。
class Event:
def __init__(self):
# 條件對(duì)象,所以事件對(duì)象其實(shí)是基于條件對(duì)象的一個(gè)封裝
self._cond = Condition(Lock())
# 標(biāo)志位,初始為 False
self._flag = False
def is_set(self):
# 標(biāo)志位是否被設(shè)置
return self._flag
isSet = is_set
def set(self):
# 修改共享變量時(shí)需要加鎖保護(hù)
with self._cond:
# 設(shè)置標(biāo)志位,并喚醒所有阻塞線程
self._flag = True
self._cond.notify_all()
def clear(self):
# 將標(biāo)志位設(shè)置為 False
with self._cond:
self._flag = False
def wait(self, timeout=None):
# 阻塞等待,但支持超時(shí)時(shí)間
with self._cond:
signaled = self._flag
if not signaled:
signaled = self._cond.wait(timeout)
return signaled非常簡(jiǎn)單,Event 內(nèi)部是基于 Condition 實(shí)現(xiàn)的,關(guān)于條件變量,我們后續(xù)再詳細(xì)介紹。
小結(jié)
以上我們就聊了聊 Event 的實(shí)現(xiàn)原理,因?yàn)椴僮飨到y(tǒng)感知不到協(xié)程,所以協(xié)程 Event 基于 Future 對(duì)象實(shí)現(xiàn)。而線程 Event 則基于條件變量,關(guān)于條件變量,我們以后再詳細(xì)展開。
到此這篇關(guān)于源碼解讀Python中Event事件的使用的文章就介紹到這了,更多相關(guān)Python Event內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python3中類的重點(diǎn)與難點(diǎn):類屬性和實(shí)例屬性的區(qū)別說明
這篇文章主要介紹了python3中類的重點(diǎn)與難點(diǎn):類屬性和實(shí)例屬性的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Python連接mssql數(shù)據(jù)庫編碼問題解決方法
這篇文章主要介紹了Python連接mssql數(shù)據(jù)庫編碼問題解決方法,本文方法同樣適用mysql、sqllite、mongodb等數(shù)據(jù)庫,需要的朋友可以參考下2015-01-01
wxPython實(shí)現(xiàn)文本框基礎(chǔ)組件
這篇文章主要介紹了wxPython實(shí)現(xiàn)文本框基礎(chǔ)組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Tensorflow2.10使用BERT從文本中抽取答案實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Tensorflow2.10使用BERT從文本中抽取答案實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
python利用xpath爬取網(wǎng)上數(shù)據(jù)并存儲(chǔ)到django模型中
這篇文章主要介紹了python利用xpath爬取網(wǎng)上數(shù)據(jù)并存儲(chǔ)到django模型中,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
Python時(shí)間差中seconds和total_seconds的區(qū)別詳解
今天小編就為大家分享一篇Python時(shí)間差中seconds和total_seconds的區(qū)別詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-12-12
Python利用matplotlib實(shí)現(xiàn)動(dòng)態(tài)可視化詳解
Python中的數(shù)據(jù)可視化是指原始數(shù)據(jù)的圖形表示,以更好地可視化、理解和推理,Python提供了各種庫,包含用于可視化數(shù)據(jù)的不同特性,下面我們就來看看如何利用matplotlib實(shí)現(xiàn)動(dòng)態(tài)可視化吧2023-08-08
編程小妙招:Python帶你玩轉(zhuǎn)Excel超鏈接
掌握Python實(shí)現(xiàn)Excel加超鏈接的技巧,讓你的數(shù)據(jù)報(bào)告活起來,本指南將帶你輕松穿梭于單元格間,一行代碼搞定鏈接,別等了,跟我一起讓你的Excel工作表不僅聰明,還能“點(diǎn)”亮你的信息網(wǎng)絡(luò)!2023-12-12

