python async await原理,python3 asyncio
本文主要详细介绍了PythonAsyncio的调度原理。Python。Asyncio是一个大而全的库,包含了很多函数。除了三种等待对象之外,与核心调度相关的逻辑还有其他函数,分别位于三个文件中:runners.py、base_event.py和event.py。
00-1010前言1。基本介绍2。2的调度实现。事件循环3。网络IO事件的处理
目录
文章《PythonAsyncio中Coroutines,Tasks,Future可等待对象的关系及作用》介绍了Python的等待对象的功能。特别是任务对象在启动时可以驱动自身,但一个任务对象只能驱动一个执行链。如果多个链同时执行,仍然需要EventLoop来安排驱动。接下来,我们将通过Python的源代码了解EventLoop是如何工作的。阿辛西奥图书馆。
前言
Python。Asyncio是一个大而全的库,包含了很多函数。除了三种等待对象,与核心调度相关的逻辑还有其他功能,分别位于runners.py、base_event.py、event.py三个文件中。
Runners.py文件有一个主类——Runner,主要职责是在进入协同模式时做好事件的循环工作直到初始化,在退出协同模式时清理协同学、生成器等还在内存中的对象。
协处理模式只是为了便于理解。对于计算机来说,没有这样的区别。
event.py文件中除了EventLoop对象的接口以及获取和设置EventLoop的函数之外,还包含了EventLoop的两个可调度对象,分别是Handler和TimerHandler,可以看作是EvnetLoop调用的其他对象的容器,用来连接待调度对象和事件循环之间的关系,但是它们的实现非常简单。对于汉德勒,它的源码如下:
#某些您不想关闭的代码已被删除
类别句柄:
def __init__(self,callback,args,loop,context=None):
#初始化上下文,确保在执行过程中可以找到句柄所在的上下文。
如果上下文为None:
context=context vars . copy _ context()
自我。_context=上下文
自我。_loop=loop
自我。_callback=回调
自我。_args=args
自我。_ cancelled=False
定义取消(自身):
#将当前句柄设置为取消。
如果不是自我。_已取消:
自我。_ cancelled=True
自我。_callback=无
自我。_args=无
定义已取消(自身):
回归自我。_已取消
def _run(自身):
#用于执行实函数,由context.run方法确保在自己的上下文中执行。
尝试:
#继续在您拥有的上下文中执行相应的回调。
自我。_context.run(self。_回调,*self。_args)
(SystemExit,KeyboardInterrupt):除外
上升
除了BaseException为exc:
cb = format_helpers._format_callback_source(
self._callback, self._args)
msg = fException in callback {cb}
context = {
message: msg,
exception: exc,
handle: self,
}
self._loop.call_exception_handler(context)
通过源码可以发现,Handle
功能十分简单,提供了可以被取消以及可以在自己所处的上下文执行的功能,而TimerHandle
继承于Handle
比Handle
多了一些和时间以及排序相关的参数,源码如下:
class TimerHandle(Handle):
通过代码可以发现,这两个对象十分简单,而我们在使用Python.Asyncio
时并不会直接使用到这两个对象,而是通过loop.call_xxx
系列方法来把调用封装成Handle
对象,然后等待EventLoop
执行。 所以loop.call_xxx
系列方法可以认为是EventLoop
的注册操作,基本上所有非IO的异步操作都需要通过loop.call_xxx
方法来把自己的调用注册到EventLoop
中,比如Task
对象就在初始化后通过调用loop.call_soon
方法来注册到EventLoop
中,loop.call_sonn
的实现很简单,
它的源码如下:
class BaseEventLoop:
可以看到call_soon
真正相关的代码只有10几行,它负责把一个调用封装成一个Handle
,并添加到self._reday
中,从而实现把调用注册到事件循环之中。
loop.call_xxx
系列函数除了loop.call_soon
系列函数外,还有另外两个方法--loop.call_at
和loop.call_later
,它们类似于loop.call_soon
,不过多了一个时间参数,来告诉EventLoop
在什么时间后才可以调用,同时通过loop.call_at
和loop.call_later
注册的调用会通过Python
的堆排序模块headpq
注册到self._scheduled
变量中,
具体代码如下:
class BaseEventLoop:
2.EventLoop的调度实现
在文章《PythonAsyncio中Coroutines,Tasks,Future可等待对象的关系及作用》中已经分析到了runner
会通过loop.run_until_complete
来调用main
Task从而开启EventLoop
的调度,所以在分析EventLoop
的调度时,应该先从loop.run_until_complete
入手,
对应的源码如下:
class BaseEventLoop:
这段源码并不复杂,它的主要逻辑是通过把Corotinue
转为一个Task
对象,然后通过Task
对象初始化时调用loop.call_sonn
方法把自己注册到EventLoop
中,最后再通过loop.run_forever
中的循环代码一直运行着,直到_stopping
被标记为True
:
while True:
可以看出,这段代码是确保事件循环能一直执行着,自动循环结束,而真正调度的核心是_run_once
函数,
它的源码如下:
class BaseEventLoop:
通过源码分析,可以很明确的知道调度逻辑中第一步是先规整self._scheduled
,在规整的过程是使用堆排序来进行的,因为堆排序在调度的场景下效率是非常高的,不过这段规整代码分成两种,我猜测是当需要取消的数量过多时直接遍历的效率会更高。 在规整self._scheduled
后,就进入第二步,该步骤开始等待系统事件循环返回对应的事件,如果self._ready
中有数据,就不做等待了,需要马上到下一步骤,以便能赶紧安排调度。 在得到系统事件循环得到的事件后,就进入到了第三步,该步骤会通过self._process_events
方法处理对应的事件,并把事件对应的回调存放到了self._ready
中,最后再遍历self._ready
中的所有Handle
并逐一执行(执行时可以认为EventLoop
把控制权返回给对应的调用逻辑),至此一个完整的调度逻辑就结束了,并进入下一个调度逻辑。
3.网络IO事件的处理
注:由于系统事件循环的限制,所以文件IO一般还是使用多线程来执行,具体见:github.com/python/asyn…
在分析EventLoop
调度实现的时候忽略了self._process_events
的具体实现逻辑,因为_process_events
方法所在asyncio.base_event.py
文件中的BaseEventLoop
类并未有具体实现的,因为网络IO相关的需要系统的事件循环来帮忙处理,所以与系统事件循环相关的逻辑都在asyncio.selector_events.py
中的BaseSelectorEventLoop
类中。BaseSelectorEventLoop
类封装了selector
模块与系统事件循环交互,使调用者不需要去考虑sock的创建以及sock产生的文件描述符的监听与注销等操作,下面以BaseSelectorEventLoop
中自带的pipe为例子,分析BaseSelectorEventLoop
是如何进行网络IO事件处理的。
在分析之前,先看一个例子,代码如下:
import asyncio
如果直接运行这个例子,它并不会输出task
(不过在IDE使用DEBUG模式下线程启动会慢一点,所以会输出的),因为在调用loop.run_forever
后EventLoop
会一直卡在这段逻辑中:
event_list = self._selector.select(timeout)
所以调用loop.call_soon
并不会使EventLoop
马上安排调度,而如果把call_soon
换成call_soon_threadsafe
则可以正常输出,这是因为call_soon_threadsafe
中多了一个self._write_to_self
的调用,它的源码如下:
class BaseEventLoop:
由于这个调用是涉及到IO相关的,所以需要到BaseSelectorEventLoop
类查看,接下来以pipe相关的网络IO操作来分析EventLoop
是如何处理IO事件的(只演示reader对象,writer对象操作与reader类似),
对应的源码如下:
class BaseSelectorEventLoop(base_events.BaseEventLoop):
通过源码中的创建部分可以看到,EventLoop
在启动的时候会创建一对建立通信的sock,并设置为非阻塞,然后把对应的回调封装成一个Handle
对象并注册到系统事件循环中(删除则进行对应的反向操作),之后系统事件循环就会一直监听对应的事件,也就是EventLoop
的执行逻辑会阻塞在下面的调用中,等待事件响应:
event_list = self._selector.select(timeout)
这时如果执行loop.call_soon_threadsafe
,那么会通过write_to_self
写入一点信息:
def _write_to_self(self):
由于csock
被写入了数据,那么它对应的ssock
就会收到一个读事件,系统事件循环在收到这个事件通知后就会把数据返回,然后EventLoop
就会获得到对应的数据,并交给process_events
方法进行处理,
它的相关代码如下:
class BaseSelectorEventLoop:
从代码中可以看出_process_events
会对事件对应的文件描述符进行处理,并从事件回调中获取到对应的Handle
对象添加到self._ready
中,由EventLoop
在接下来遍历self._ready
并执行。
可以看到网络IO事件的处理并不复杂,因为系统事件循环已经为我们做了很多工作了,但是用户所有与网络IO相关的操作都需要有一个类似的操作,这样是非常的繁琐的,幸好asyncio
库已经为我们做了封装,我们只要调用就可以了,方便了很多。
到此这篇关于Python Asyncio调度原理详情的文章就介绍到这了,更多相关Python Asyncio 内容请搜索盛行IT软件开发工作室以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT软件开发工作室!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。