这个问题是由我的另一个问题推动的:如何在cdef中等待?
网上有大量的文章和博客文章asyncio,但它们都非常肤浅.我找不到任何有关如何asyncio实际实现的信息,以及I/O异步的原因.我试图阅读源代码,但它是成千上万行不是最高级别的C代码,其中很多处理辅助对象,但最重要的是,很难在Python语法和它将翻译的C代码之间建立连接成.
Asycnio自己的文档甚至没那么有用.没有关于它是如何工作的信息,只有关于如何使用它的一些指导,这些指导有时也会误导/写得很差.
我熟悉Go的coroutines实现,并且希望Python做同样的事情.如果是这种情况,我在上面链接的帖子中出现的代码就可以了.既然没有,我现在正试图找出原因.到目前为止我最好的猜测如下,请纠正我错在哪里:
async def foo(): ...实际上被解释为类继承的方法coroutine.async def实际上是通过await语句拆分成多个方法,其中调用这些方法的对象能够跟踪到目前为止通过执行所做的进度.await语句) ).换句话说,这是我尝试将某些asyncio语法"贬低"为更容易理解的东西:
async def coro(name):
print('before', name)
await asyncio.sleep()
print('after', name)
asyncio.gather(coro('first'), coro('second'))
# translated from async def coro(name)
class Coro(coroutine):
def before(self, name):
print('before', name)
def after(self, name):
print('after', name)
def __init__(self, name):
self.name = name
self.parts = self.before, self.after
self.pos = 0
def __call__():
self.parts[self.pos](self.name)
self.pos += 1
def …Run Code Online (Sandbox Code Playgroud) 是否有一些编程模式可以让我在以下意义上检测异步事件循环何时变为空闲?假设我的执行路径以某种复杂的方式分支,比如使用 asyncio.gather(),但我知道每个分支最终都会等待一些空闲的协程,例如套接字或子进程。假设我知道这些协程实际上永远不会让步,因此事件循环将执行它可以执行的任何 Python 代码,但最终只会等待那些空闲的协程。是否有一种编程方式来检测这种状态并停止循环?
如果我使用异步函数,那么堆栈上方的所有函数也应该是异步的,并且其调用之前应带有await关键字。此示例使用应用程序的多个体系结构层模拟现代程序:
async def func1():
await asyncio.sleep(1)
async def func2():
await func1()
async def func3():
await func2()
async def func4():
await func3()
async def func5():
await func4()
Run Code Online (Sandbox Code Playgroud)
当执行线程遇到“等待”时,它可以切换到另一个协程,这需要用于上下文切换的资源。由于存在大量相互竞争的规则和不同级别的抽象,这些开销可能开始限制整个系统的性能。但是在给出的示例中,仅在一种情况下在线切换上下文是有意义的:
await asyncio.sleep(1)
Run Code Online (Sandbox Code Playgroud)
如何禁止某些异步功能的上下文切换?
据我了解,目的是同时asyncio.gather运行其参数,并且当协程执行等待表达式时,它为事件循环提供了安排其他任务的机会。考虑到这一点,我惊讶地发现以下代码片段忽略了.asyncio.gather
import asyncio
async def aprint(s):
print(s)
async def forever(s):
while True:
await aprint(s)
async def main():
await asyncio.gather(forever('a'), forever('b'))
asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)
据我了解,会发生以下情况:
实际上,这不是我所观察到的。相反,整个程序相当于while True: print('a'). 我发现非常有趣的是,即使对代码进行微小的更改似乎也会重新引入公平性。例如,如果我们使用以下代码,那么我们会在输出中得到大致相等的“a”和“b”混合。
async def forever(s):
while True:
await aprint(s)
await asyncio.sleep(1.)
Run Code Online (Sandbox Code Playgroud)
验证它似乎与我们在无限循环中和在无限循环外花费的时间没有任何关系,我发现以下更改也提供了公平性。
async def forever(s):
while True:
await aprint(s)
await asyncio.sleep(0.)
Run Code Online (Sandbox Code Playgroud)
有谁知道为什么会发生这种不公平现象以及如何避免它?我想,当有疑问时,我可以主动在各处添加一个空的睡眠语句,并希望这足够了,但对我来说,为什么原始代码的行为不符合预期,这对我来说非常不明显。
以防万一,因为 asyncio 似乎已经经历了相当多的 API 更改,我在 Ubuntu 机器上使用 Python 3.8.4 的普通安装。