源码解读Python中Event事件的使用

 更新时间:2023年12月22日 09:14:30   作者:古明地觉的编程教室  
事件(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 为红灯。

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

当标志位是 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中Event事件的使用的文章就介绍到这了,更多相关Python Event内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Pandas根据条件实现替换列中的值

    Pandas根据条件实现替换列中的值

    在使用Pandas的Python中,DataFrame列中的值可以通过使用各种内置函数根据条件进行替换,本文主要来和大家讨论在Pandas中用条件替换数据集列中的值的各种方法,希望对大家有所帮助
    2024-01-01
  • 浅析python函数式编程

    浅析python函数式编程

    这篇文章主要介绍了python函数式编程的相关资料,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下
    2020-09-09
  • Python实现全自动安装第三方库的方法

    Python实现全自动安装第三方库的方法

    这篇文章主要介绍了Python实现全自动安装第三方库的方法,一说Python要安装哪个模块,我们第一反应,win+r输入cmd,pip instll安装,但是如果换电脑了,Python第三方库那么多,难道要一次一次的敲击pip install,本文就介绍一个简单的方法解放双手,需要的朋友可以参考下
    2023-07-07
  • python 字典的概念叙述和使用方法

    python 字典的概念叙述和使用方法

    Python中还有一个很重要的数据类型就是字典,其实集合的底层使用的也是字典,这篇文章主要介绍了python 字典的概念叙述和使用方法,需要的朋友可以参考下
    2023-02-02
  • python读取图片并修改格式与大小的方法

    python读取图片并修改格式与大小的方法

    这篇文章主要为大家详细介绍了python读取图片并修改格式与大小的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • python 使用csv模块读写csv格式文件的示例

    python 使用csv模块读写csv格式文件的示例

    这篇文章主要介绍了python 使用csv模块读写csv格式文件的示例,帮助大家更好的理解和学习python,感兴趣的朋友可以了解下
    2020-12-12
  • python内置数据类型之列表操作

    python内置数据类型之列表操作

    数据类型是一种值的集合以及定义在这种值上的一组操作。这篇文章主要介绍了python内置数据类型之列表的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-11-11
  • 在Django中创建自己的自定义用户模型

    在Django中创建自己的自定义用户模型

    这篇文章主要介绍了在Django中创建自己的自定义用户模型,创建自己的自定义用户模型至关重要。将来,如果要对模型进行一些更改,则可以轻松进行这些更改。不然我们可能必须对模型进行一些更改,而且代码的某些部分也将被更改,下面一起进入文章里哦阿姐个表格的详细内容吧
    2022-01-01
  • python将logging模块封装成单独模块并实现动态切换Level方式

    python将logging模块封装成单独模块并实现动态切换Level方式

    这篇文章主要介绍了python将logging模块封装成单独模块并实现动态切换Level方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • OpenCV4.1.0+VS2017环境配置的方法步骤

    OpenCV4.1.0+VS2017环境配置的方法步骤

    这篇文章主要介绍了OpenCV4.1.0+VS2017环境配置的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07

最新评论