Python async和await关键字究竟是如何工作的?什么';在等待链的末端是什么?
我有以下代码:Python async和await关键字究竟是如何工作的?什么';在等待链的末端是什么?,python,python-3.x,async-await,python-asyncio,Python,Python 3.x,Async Await,Python Asyncio,我有以下代码: async def foo(x): yield x yield x + 1 async def intermediary(y): await foo(y) def bar(): c = intermediary(5) 要从c中得到5和6,我应该在条中输入什么 我这样问是因为asyncio库看起来很神奇。我想知道魔法到底是怎么起作用的 也许我想编写自己的函数,调用read或write,然后通知我编写的某个顶级循环,它们正在等待文件描述符变得可读
async def foo(x):
yield x
yield x + 1
async def intermediary(y):
await foo(y)
def bar():
c = intermediary(5)
要从c
中得到5和6,我应该在条中输入什么
我这样问是因为asyncio
库看起来很神奇。我想知道魔法到底是怎么起作用的
也许我想编写自己的函数,调用read
或write
,然后通知我编写的某个顶级循环,它们正在等待文件描述符变得可读或可写
然后,也许我希望,一旦这些条件变为现实,顶层循环能够恢复我的读写函数(以及顶层循环和它们之间的整个中间链)
我已经或多或少知道如何使用asyncio
。我写这篇文章是为了在延迟后计算平方,但在一个随机间隔后启动许多任务,每个任务都附加到一个列表中。它写得有点笨拙,但它很管用
我想知道这个项目到底在做什么。为了做到这一点,我必须知道wait on the sleep是如何通知顶级事件循环它想休眠(并再次被调用)一段时间的,以及调用sleep和顶级事件循环之间的所有中间堆栈帧的状态是如何被冻结的,然后在延迟结束时重新激活的,看起来和你想做的差不多。它包含一个睡眠调用(用来代替IO),因此asyncio特性是有意义的
import asyncio
async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(1.0)
return x + y
async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
有一个,看起来几乎和你想做的完全一样。它包含一个睡眠调用(用来代替IO),因此asyncio特性是有意义的
import asyncio
async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(1.0)
return x + y
async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
您是否尝试过查看asyncio.sleep的源代码
@coroutine
def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay == 0:
yield
return result
if loop is None:
loop = events.get_event_loop()
future = loop.create_future()
h = future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return (yield from future)
finally:
h.cancel()
基本上,它使用loop.call\u later设置未来,然后等待未来。不确定这是否完全回答了您的问题,但它可能会有所帮助。您是否尝试查看asyncio.sleep的源代码
@coroutine
def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay == 0:
yield
return result
if loop is None:
loop = events.get_event_loop()
future = loop.create_future()
h = future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return (yield from future)
finally:
h.cancel()
基本上,它使用loop.call\u later设置未来,然后等待未来。不确定这是否完全回答了您的问题,但它可能会有所帮助。仔细阅读您上面提供的代码,包含
收益率的异步def
会创建:
要使用其中的数据,请使用async for
:
async def intermediary(y):
results = []
async for x in foo(y):
results.append(x)
return results
要从一个普通函数中使用一个简单的协同程序(如中介
)的结果,您需要创建一个事件循环,并使用运行直到\u完成()
:
仔细阅读上面提供的代码,包含产量的异步def
会创建:
要使用其中的数据,请使用async for
:
async def intermediary(y):
results = []
async for x in foo(y):
results.append(x)
return results
要从一个普通函数中使用一个简单的协同程序(如中介
)的结果,您需要创建一个事件循环,并使用运行直到\u完成()
:
所以,我更了解如何让我想做的事情发挥作用。我的代码应该是这样读的:
import types
@types.coroutine
def foo(x):
yield x
yield x + 1
async def intermediary(y):
await foo(y)
def bar():
c = intermediary(5)
try:
while True:
result = c.send(None)
print(f"Got {result} from the coroutine.")
except StopIteration as e:
print(f"StopIteration exception: {e!r}")
基本的答案是,它的端点可以是一个用类型修饰的普通生成器。coroutine
。有更多的方法可以实现这一点,对我的代码的进一步修改说明了这一点:
import types
from collections.abc import Awaitable
@types.coroutine
def foo(x):
sent = yield x
print(f"foo was sent {sent!r}.")
sent = yield x + 1
print(f"foo was sent {sent!r}.")
return 'generator'
class MyAwaitable(Awaitable):
def __init__(self, x):
super().__init__()
self.x_ = x
def __await__(self):
def gen(x):
for i in range(x-1, x+2):
sent = yield i
print(f"MyAwaitable was sent {sent!r}.")
return 'class'
return iter(gen(self.x_))
async def intermediary(t, y):
awaited = await t(y)
print(f"Got {awaited!r} as value from await.")
def runco(chain_end):
c = intermediary(chain_end, 5)
try:
sendval = None
while True:
result = c.send(sendval)
print(f"Got {result} from the coroutine.")
sendval = sendval + 1 if sendval is not None else 0
except StopIteration as e:
print(f"StopIteration exception: {e!r}")
正如您所看到的,任何定义返回迭代器的\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法的东西都可以被等待。真正发生的情况是,正在等待的对象被迭代,直到停止,然后等待返回。这样做的原因是,链末端的最后一件事可能会遇到某种阻塞条件。然后,它可以通过yield
ing或从迭代器返回值(基本上与yield
ing相同)报告该条件(或请求设置回调或其他内容)。然后顶层循环可以继续运行到任何其他可以运行的东西
整个wait
调用链的本质是,当您返回并从迭代器中请求下一个值时(回调被阻止的函数,告诉它现在可能没有被阻止),整个调用堆栈被重新激活。整个链的存在是为了在调用被阻止时保持调用堆栈的状态。基本上是一个主动放弃控制而不是让调度程序从中夺取控制的线程
当我问这个问题时,我脑海中关于asyncio
如何在内部工作的愿景显然是调用的东西是如何工作的,并且基于端点例程yield
ing某种指示器,指示它们被什么阻止,以及运行它的顶级循环(在我的示例中是runco
)然后把它放在某种通用的条件池中去寻找,这样一旦条件被改变阻塞,它就可以恢复例程。在asyncio
中,发生了更复杂的事情,它使用带有\uuuuuwait\uuuuuuu
方法的对象(如我的示例中的mywaitable
)和某种回调机制来实现这一切
Brett Cannon写了一篇非常好的文章,谈到了。这将比我在回答中所能说的要详细得多
我发现了一个有趣的小道消息,当你这么做的时候:
def foo(x):
yield 11
bar = types.coroutine(foo)
foo
和bar
都成为“协同程序”,可以wait
打开。decorator所做的只是在foo.\uuuu code\uuuu.co\u标志中翻转一点
。当然,这是一个实现细节,不应依赖。我认为这实际上是一个bug,我可能会这样报告。因此,我更了解如何使我试图做的工作起作用。我的代码应该是这样读的:
import types
@types.coroutine
def foo(x):
yield x
yield x + 1
async def intermediary(y):
await foo(y)
def bar():
c = intermediary(5)
try:
while True:
result = c.send(None)
print(f"Got {result} from the coroutine.")
except StopIteration as e:
print(f"StopIteration exception: {e!r}")
基本的答案是,它的端点可以是一个用类型修饰的普通生成器。coroutine
。有更多的方法来实现这一点,还有对我的代码的进一步修改