简单了解一下事件循环(Event Loop)

时间:2022-06-12 09:42:56

关于我

一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。

Github:https://github.com/hylinux1024

微信公众号:终身开发者(angrycode)

0x00 事件循环(Event Loop)

在前文《为何你还不懂得如何使用Python协程

中提到协程是通过asyncio包中的高级API来启动的。而asyncio模块中的核心就是事件循环(Event Loop)。它可用于执行异步任务、事件回调、执行网络IO操作和运行子进程。官方的文档也是建议开发者应该尽量使用asyncio包提供的高级的API,避免直接使用Event Loop对象中的方法。

系统提供的底层能力的功能模块例如网络连接、文件IO等都会使用到loop

大多数情况下,这些高级API可以满足众多使用场景,但作为一个有追求的猿类,应该要有一点点探索的精神,看看在asyncio封装之下的Event Loop

获取Event Loop对象
  • asyncio.get_running_loop()

    获取当前系统线程正在使用的loop对象
  • asyncio.get_event_loop()

    获取当前正在使用的loop对象。如果当前系统线程还没有loop对象,那么就会创建一个新的loop对象,并使用asyncio.set_event_loop(loop)方法设置到当前系统线程中。
  • asyncio.new_event_loop()

    创建一个新的loop对象
  • asyncio.set_event_loop(loop)

    loop设置成系统线程使用的对象

Event Loop对象的常用方法

如果使用类似asyncio.run()这些高级API,以下这些方法,基本上很少会用到,建议通读一下,大概知道loop对象拥有哪些API,了解以下底层的实现细节。

启动和停止
  • loop.run_until_complete(future)

    future对象执行完成才返回
  • loop.run_forever()

    一直运行,直到调用了loop.stop()方法
  • loop.stop()

    停止loop对象
  • loop.is_running()

    判断loop是否正在运行
  • loop.is_closed()

    判断loop是否关闭
  • loop.close()

    关闭loop对象
  • coroutine loop.shutdown_asyncgens()
try:
loop.run_forever()
finally:
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()
回调方法
  • loop.call_soon(callback, *args, context=None)

    在事件循环的下一次迭代中执行callback方法,args是方法中的参数
  • loop.call_soon_threadsafe(callback, *args, context=None)

    线程安全的call_soon()
iimport asyncio
import time def hello_world(loop):
print('Hello World')
time.sleep(3) # 模拟长时间操作
loop.stop() loop = asyncio.get_event_loop() # 使用loop执行 hello_world()
loop.call_soon(hello_world, loop) # 会一直阻塞,直到调用了stop方法
try:
loop.run_forever()
finally:
loop.close()
可延迟的回调方法

可设置延迟执行的方法

  • loop.call_later(delay, callback, *args, context=None)

    延迟delay秒后执行
  • loop.call_at(when, callback, *args, context=None)

    在指定时间点执行
  • loop.time()

    返回当前时间
import asyncio
import datetime def display_date(end_time, loop):
print(datetime.datetime.now())
if (loop.time() + 1.0) < end_time:
# 1秒后执行
loop.call_later(1, display_date, end_time, loop)
else:
loop.stop() loop = asyncio.get_event_loop() # 执行5秒
end_time = loop.time() + 5.0
loop.call_soon(display_date, end_time, loop) # 一直运行,等待stop的调用
try:
loop.run_forever()
finally:
loop.close()

执行结果打印5秒时间点

2019-05-09 22:34:47.885412
2019-05-09 22:34:48.887513
2019-05-09 22:34:49.889396
2019-05-09 22:34:50.894316
2019-05-09 22:34:51.898457
创建Future和Tasks
  • loop.create_future()

    创建一个绑定事件循环的future对象
  • loop.create_task(coro)

    coro放入调度,并返回task对象
  • loop.set_task_factory(factory)

    设置任务工厂
  • loop.get_task_factory()

    返回任务工厂,有可能返回None
创建网络连
coroutine loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None)

指定hostport等参数创建网络连接

coroutine loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None)

创建UDP连接

coroutine loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None)

创建Unix连接

coroutine loop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True)

创建TCP服务

coroutine loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True)

创建Unix服务

coroutine loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None)

封装已建立的连接,返回元组(transport, protocol)

Event Loop 的实现

asyncio 的事件循环有两种不同的实现:SelectorEventLoopProactorEventLoop,它们的父类是AbstractEventLoop

SelectorEventLoop

这个是asyncio默认使用的Event Loop实现,支持unixwindows平台

import asyncio
import selectors selector = selectors.SelectSelector()
loop = asyncio.SelectorEventLoop(selector)
asyncio.set_event_loop(loop)
ProactorEventLoop

这个是Windows平台专有的实现

import asyncio
import sys if sys.platform == 'win32':
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)

0x01 总结

事件循环是asyncio的核心,asncio模块的很多高级接口是通过封装Event Loop对象来实现的。它提供了执行异步任务、事件回调、执行网络IO操作和运行子进程的能力。

本文是通过官方文档对事件循环的概念和它的常见API做了一个大概的了解。作为《前文》的补充

0x02 引用

  1. https://docs.python.org/3/library/asyncio-eventloop.html

简单了解一下事件循环(Event Loop)的更多相关文章

  1. JS事件循环&lpar;Event Loop&rpar;机制

    前言 众所周知,为了与浏览器进行交互,Javascript是一门非阻塞单线程脚本语言. 为何单线程? 因为如果在DOM操作中,有两个线程一个添加节点,一个删除节点,浏览器并不知道以哪个为准,所以只能选 ...

  2. 事件循环 event loop 究竟是什么

    事件循环 event loop 究竟是什么 一些概念 浏览器运行时是多进程,从任务管理器或者活动监视器上可以验证. 打开新标签页和增加一个插件都会增加一个进程,如下图:  浏览器渲染进程是多线程,包 ...

  3. 事件循环Event loop到底是什么

    摘要:本文通过结合官方文档MDN和其他博客深入解析浏览器的事件循环机制,而NodeJS有另一套事件循环机制,不在本文讨论范围中.process.nextTick和setImmediate是NodeJS ...

  4. JavaScipt 中的事件循环&lpar;event loop&rpar;,以及微任务 和宏任务的概念

    说事件循环(event loop)之前先要搞清楚几个问题. 1. js为什么是单线程的? 试想一下,如果js不是单线程的,同时有两个方法作用dom,一个删除,一个修改,那么这时候浏览器该听谁的?   ...

  5. 浏览器与Node的事件循环&lpar;Event Loop&rpar;有何区别&quest;

    前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的. 一.线程与进程 1. 概念 我们经常说 JS 是单线程执行的,指的是一个进程里 ...

  6. JavaScript 事件循环 — event loop

    引言 相信所有学过 JavaScript 都知道它是一门单线程的语言,这也就意味着 JS 无法进行多线程编程,但是 JS 当中却有着无处不在的异步概念 .在初期许多人会把异步理解成类似多线程的编程模式 ...

  7. 一文梳理JavaScript 事件循环&lpar;Event Loop&rpar;

    事件循环(Event Loop),是每个JS开发者都会接触到的概念,但是刚接触时可能会存在各种疑惑. 众所周知,JS是单线程的,即同一时间只能运行一个任务.一般情况下这不会引发问题,但是如果我们有一个 ...

  8. JavaScript事件循环&lpar;Event Loop&rpar;机制

    JavaScript 是单线程单并发语言 什么是单线程 主程序只有一个线程,即同一时间片断内其只能执行单个任务. 为什么选择单线程? JavaScript的主要用途是与用户互动,以及操作DOM.这决定 ...

  9. JavaScript:彻底理解同步、异步和事件循环&lpar;Event Loop&rpar; (转)

    原文出处:https://segmentfault.com/a/1190000004322358 一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS ...

随机推荐

  1. 史上最强大的40多个纯CSS绘制的图形

    Square(正方形) #square { width: 100px; height: 100px; background: red; } Rectangle(矩形) #rectangle { wid ...

  2. 今天踩过的坑——structs和phpmyadmin

    phpmyadmin 错误:缺少 mcrypt 扩展解决mv -i /etc/php5/conf.d/mcrypt.ini /etc/php5/mods-available/sudo php5enmo ...

  3. 用WIN7系统IIS的提示:数据库连接出错,请检查Conn&period;asp文件中的数据库参数设置

    我用科讯的从4.0开始,去年开始很少用科讯做新站了,今天拿来做一下,结果悲剧了,数据库路径老是不对,百度一番又一番的,,最后终于给度娘解决了.分享出来给遇到同样的问题的人. 用WIN7系统IIS的注意 ...

  4. 页面modal服务

    /** * * * 初始化: * var oneModal = modalSvc.createModal(templateUrl, controller, size); * size可以是:lg或sm ...

  5. &period;net框架介绍

    .net 一般指 .net Framework框架,是一种平台,一种技术 C# 是一种编程语言,可以开发基于.net平台的应用 Java 既是一种技术,也是一种语言 .net可以开发桌面应用程序    ...

  6. 转:loadruner报错&colon;Step download timeout&lpar;120 seconds&rpar;的一个解决方法

    一个网友问了我一个问题如下:loadruner报错:Error -27728: Step download timeout (120 seconds) 如何解决语法检查通过,但是在并发执行一个查询时候 ...

  7. 强连通分量tarjan缩点——POJ2186 Popular Cows

    这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定 ...

  8. c博客作业--分支、顺序结构

    1.本章学习总结 1.1思维导图 1.2本章学习体会及代码量学习体会 1.2.1学习体会 对于本章学习我感觉对代码有了初步的了解,一些简单的题目可以熟练掌握,但现在解决一道题目花的时间过多,不易发现那 ...

  9. 分页插件pagination&period;js

    项目中有分页功能,之前都是自己写,样式不好看,功能也简单,就找了这个插件pagination.js 页面导入pagination.js html代码 <div class="list_ ...

  10. C&plus;&plus;程序设计方法3:虚函数

    向上映射与向下映射 派生类对象转换成基类对象,称为向上映射. 而基类对象转换成派生类对象,称为向下映射: 向上映射可由编译器自动完成,是一种隐式的自动类型转化: 凡是接受基类对象的地方(如函数参数)都 ...