关于我

一个有思想的程序猿,终身学习实践者,目前在一个创业团队任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事件循环(Event Loop)机制

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

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

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

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

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

  4. JavaScipt 中的事件循环(event loop),以及微任务 和宏任务的概念

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

  5. 浏览器与Node的事件循环(Event Loop)有何区别?

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

  6. JavaScript 事件循环 — event loop

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

  7. 一文梳理JavaScript 事件循环(Event Loop)

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

  8. JavaScript事件循环(Event Loop)机制

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

  9. 事件循环Event Loop

    在 事件循环 期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息.被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数.正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧. ...

随机推荐

  1. 关于css样式加载的问题

    今天我在学习jQuery的addClass操作时遇到了一个小问题,想来跟大家分享一下,避免初学者踩坑. 我的需求是制作一个表格,并让它隔行换色,在此基础上再加上鼠标悬浮变色的效果.(主要训练jQuer ...

  2. 从后端到前端之Vue(二)写个tab试试水

    上一篇写了一下table,然后要写什么呢?当然是tab了.动态创建一个tab,里面放一个table,这样一个后台管理的基本功能(之一)就出来了. 好吧,这里其实只是试试水,感受一下vue的数据驱动可以 ...

  3. 多个数据库 migration

    More than one DbContext was found. Specify which one to use. Use the '-Context' parameter for PowerS ...

  4. TF项目实战(SSD目标检测)-VOC2007

    TF项目实战(SSD目标检测)-VOC2007 训练好的模型和代码会公布在网上: 步骤: 1.代码地址:https://github.com/balancap/SSD-Tensorflow 2.解压s ...

  5. MapRedue详细工作流程

    MapRedue详细工作流程 简述 (1)客户端submit之前获取待处理的数据信息,根据参数配置,形成一个任务分配的规划. (2)提交切片信息到YARN(split.xml,job.split,wc ...

  6. Android 设置ImageView全屏

    Android 设置ImageView全屏代码如下: <ImageView android:id="@+id/iv_image" android:scaleType=&quo ...

  7. MongoDB 启动时关于 NUMA 警告 的分析----(To avoid performance problems)

    1. 需求描述 观察MongoDB的启动Log,会看到一个关于  NUMA 的警告 和 优化建议 --17T17:: I CONTROL [initandlisten] ** WARNING: You ...

  8. 整合SSM框架必备基础—SpringMVC(下)

    在上一篇文章<整合SSM框架必备基础-SpringMVC(上)>中,胖达介绍了关于SpringMVC的诞生.优势以及执行流程等理论知识点,这篇文章打算在实操中加深一下对SpringMVC的 ...

  9. linux初学者-系统启动故障篇

    linux初学者-系统启动故障篇 在系统的操作中,有时会不小心误删或者操作失误使得系统启动不起来,下文将列举几种常见的系统启动失败的情况及解决的办法. 1.删除或者覆盖mbr的446个字节 mbr的4 ...

  10. zookeeper集群搭建及常用场景实现

    本文完整源码地址 基于zookeeper的常用用法.分布式锁.分布式队列及leader选举实现 https://github.com/killianxu/zookeeper_example zooke ...