源码解密 Python 的 Event

简介: 源码解密 Python 的 Event

之前我们介绍了锁、信号量以及队列,那么本次来说一说事件(Event),它负责多任务之间的同步。

Event 对象内部维护了一个标志,初始时为 False,如果调用 event.set(),可以将它设置为 True, 调用 event.clear() 可以重置为 False。

然后 Event 对象还有一个 wait() 方法,如果内部的标志为 False,那么调用该方法会阻塞。而当标志被设置为 True(通过 set 方法)时,所有任务会解除阻塞并继续执行。

因此 Event 对象(事件)常用于多任务之间的协调和同步,例如一个任务在等待某个事件发生,而另一个任务在发生时将标志设置为 True,以此来通知正在等待的任务。

下面来实际看一下 Event 对象,另外 Event 有协程 Event 和线程 Event,我们分别介绍。


协程 Event



协程 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()
    # 任务开始执行
    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 为红灯。

然后再来看看它的源码实现:、

“”】
f3a3a9d1dd3c3720846097d8c4cb5b97.png


当标志位是 False,协程调用 wait 方法会陷入阻塞,那么阻塞要如何实现呢?没错,还是要通过 Future 对象。所以 Future 对象和 asyncio 的实现紧密相关,协程里面的阻塞等待都是基于 Future 实现的。

而 _waiters 负责保存协程内部创建的 Future 对象,_value 则表示标志位。至于 _loop 则用于指定事件循环,这个参数已经废弃了。

5305769909335d277321ed978a104639.png

is_set() 方法用于查看标志位,clear() 方法用于将标志位设置为 False,比较简单。

然后是 wait() 方法,如果调用时发现标志位是 True,那么说明是绿灯,直接通过。否则说明是红灯,于是创建一个 Future 对象,并添加到 _waiters 中,然后 await 它,从而陷入阻塞。

最后是 set() 方法,如果标志位是 False,将其设置为 True。然后将 _waiters 里面的 future 依次弹出,设置结果集,让 await fut 的协程解除阻塞。

整个过程没有任何难度,非常简单。


线程 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
"""

用法和协程 Event 几乎没什么区别,然后看一下它的内部实现。

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 实现的,关于条件变量,我们后续再详细介绍。


小结



以上我们就聊了聊 Event 的实现原理,因为操作系统感知不到协程,所以协程 Event 基于 Future 对象实现。而线程 Event 则基于条件变量,关于条件变量,我们以后再详细展开。

相关文章
|
16天前
|
Python
用python进行视频剪辑源码
这篇文章提供了一个使用Python进行视频剪辑的源码示例,通过结合moviepy和pydub库来实现视频的区间切割和音频合并。
31 2
|
2天前
|
自然语言处理 Java 编译器
为什么要看 Python 源码?它的结构长什么样子?
为什么要看 Python 源码?它的结构长什么样子?
9 2
|
1天前
|
数据采集 前端开发 Python
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
11 0
|
2月前
|
Ubuntu Linux 数据安全/隐私保护
使用Cython库包对python的py文件(源码)进行加密,把python的.py文件生成.so文件并调用
本文介绍了在Linux系统(Ubuntu 18.04)下将Python源代码(`.py文件`)加密为`.so文件`的方法。首先安装必要的工具如`python3-dev`、`gcc`和`Cython`。然后通过`setup.py`脚本使用Cython将`.py文件`转化为`.so文件`,从而实现源代码的加密保护。文中详细描述了从编写源代码到生成及调用`.so文件`的具体步骤。此方法相较于转化为`.pyc文件`提供了更高的安全性。
44 2
|
2月前
|
测试技术 Python
python自动化测试中装饰器@ddt与@data源码深入解析
综上所述,使用 `@ddt`和 `@data`可以大大简化写作测试用例的过程,让我们能专注于测试逻辑的本身,而无需编写重复的测试方法。通过讲解了 `@ddt`和 `@data`源码的关键部分,我们可以更深入地理解其背后的工作原理。
36 1
|
2月前
|
JSON 算法 API
京东以图搜图功能API接口调用算法源码python
京东图搜接口是一款强大工具,通过上传图片即可搜索京东平台上的商品。适合电商平台、比价应用及需商品识别服务的场景。使用前需了解接口功能并注册开发者账号获取Key和Secret;准备好图片的Base64编码和AppKey;生成安全签名后,利用HTTP客户端发送POST请求至接口URL;最后解析JSON响应数据以获取商品信息。
|
2月前
|
开发者 Python
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
74 1
|
2月前
|
开发者 Python
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
134 1
|
2月前
|
数据可视化 数据挖掘 索引
【python】Python马铃薯批发市场交易价格数据分析可视化(源码+数据集)【独一无二】
【python】Python马铃薯批发市场交易价格数据分析可视化(源码+数据集)【独一无二】
|
2月前
|
算法 关系型数据库 程序员
程序员必备技能)基于Python的鼠标与键盘控制实战扩展与源码
这篇文章是关于如何使用Python的`pyautogui`库来控制鼠标和键盘进行各种操作,包括移动、点击、滚轮控制以及键盘的按键和快捷键输出,并介绍了如何结合图像处理和计算机视觉技术来扩展其应用。