源码解密 Python 的 Event

本文介绍了Event对象在多任务编程中的作用,包括其内部标志机制、wait()方法的阻塞与解除,以及协程Event和线程Event的使用。重点讲解了Event如何通过Future和Condition实现同步协调。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天来说一说事件(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 为红灯。

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

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

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

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 则基于条件变量,关于条件变量,我们以后再详细展开。

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述
若有侵权,请联系删除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值