await 如何在协程链接期间将控制权交还给事件循环?
How does await give back control to the event loop during coroutine chaining?
我正在尝试使用 Python 3.6 中的 asyncio,但很难弄清楚为什么这段代码会以现在的方式运行。
示例代码:
import asyncio
async def compute_sum(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(5)
print("Returning sum")
return x + y
async def compute_product(x, y):
print("Compute %s x %s ..." % (x, y))
print("Returning product")
return x * y
async def print_computation(x, y):
result_sum = await compute_sum(x, y)
result_product = await compute_product(x, y)
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_computation(1, 2))
输出:
Compute 1 + 2 ...
Returning sum
Compute 1 x 2 ...
Returning product
1 + 2 = 3
1 * 2 = 2
预期输出:
Compute 1 + 2 ...
Compute 1 x 2 ...
Returning product
Returning sum
1 + 2 = 3
1 * 2 = 2
我对预期输出的推理:
虽然 compute_sum 协程在 compute_product 协程之前被正确调用,但我的理解是一旦我们点击 await asyncio.sleep(5)
,控制将被传回事件循环,这将开始执行 compute_product 协程。为什么 "Returning sum" 在我们命中 compute_product 协程中的 print 语句之前被执行?
关于协程的工作原理,你是对的;您的问题在于您如何 呼叫 他们。特别是:
result_sum = await compute_sum(x, y)
这会调用协程 compute_sum
然后等待它完成。
因此,compute_sum
确实在 await asyncio.sleep(5)
中屈服于调度程序,但没有其他人可以唤醒。您的 print_computation
coro 已经在等待 compute_sum
。而且还没有人开始 compute_product
,所以它肯定不能 运行。
如果你想启动多个协程并同时运行,不要await
一个;你需要一起等待他们中的很多人。例如:
async def print_computation(x, y):
awaitable_sum = compute_sum(x, y)
awaitable_product = compute_product(x, y)
result_sum, result_product = await asyncio.gather(awaitable_sum, awaitable_product)
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
(awaitable_sum
是裸协程、Future
对象还是其他可以被 await
ed 的对象都无关紧要;gather
可以工作方式。)
或者,也许更简单:
async def print_computation(x, y):
result_sum, result_product = await asyncio.gather(
compute_sum(x, y), compute_product(x, y))
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
参见示例部分中的 Parallel execution of tasks。
这是它的工作原理。
让我们使用主线程作为主要参考...
主线程处理来自不同位置的事件和工作。如果其他线程同时触发了 3 个事件,则主线程一次只能处理一个。如果主线程正在处理您的循环,它将继续处理它,直到返回方法(或函数),然后再处理其他工作。
这意味着 'other work' 被放置在队列中,以便在主线程上成为 运行。
当您使用 'async await' 时,您会写 'async' 以告知该方法将(或可以)分解到它自己的一组队列中。然后当你说 'await' 它应该在另一个线程上工作。当它执行时,允许主线程处理存储在队列中的其他事件和工作,而不是仅仅在那里等待。
因此,当 await 工作完成时,它会将方法的剩余部分也放入主线程的队列中。
因此,在这些方法中,它不会继续处理,而是将剩余的工作放入等待完成时待完成的队列中。因此它是有序的。 await compute_sum(x, y)
将控制权交还给主线程以执行其他工作,当它完成后,其余的将添加到待处理的队列中。因此 await compute_product(x, y)
在前者完成后排队。
在 , what asyncio.gather()
does behind the scenes is that it wraps each coroutine in a Task
上展开,它表示正在后台完成的工作。
你可以把Task
对象当成Future
对象,代表一个可调用对象在不同线程中的执行,只是协程是不是对线程的抽象。
并且 ThreadPoolExecutor.submit(fn)
, a Task
can be created using asyncio.ensure_future(coro())
以同样的方式创建了 Future
个实例。
通过在等待之前将所有协程安排为任务,您的示例按预期工作:
async def print_computation(x, y):
task_sum = asyncio.ensure_future(compute_sum(x, y))
task_product = asyncio.ensure_future(compute_product(x, y))
result_sum = await task_sum
result_product = await task_product
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
输出:
Compute 1 + 2 ...
Compute 1 x 2 ...
Returning product
Returning sum
1 + 2 = 3
1 * 2 = 2
我正在尝试使用 Python 3.6 中的 asyncio,但很难弄清楚为什么这段代码会以现在的方式运行。
示例代码:
import asyncio
async def compute_sum(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(5)
print("Returning sum")
return x + y
async def compute_product(x, y):
print("Compute %s x %s ..." % (x, y))
print("Returning product")
return x * y
async def print_computation(x, y):
result_sum = await compute_sum(x, y)
result_product = await compute_product(x, y)
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_computation(1, 2))
输出:
Compute 1 + 2 ...
Returning sum
Compute 1 x 2 ...
Returning product
1 + 2 = 3
1 * 2 = 2
预期输出:
Compute 1 + 2 ...
Compute 1 x 2 ...
Returning product
Returning sum
1 + 2 = 3
1 * 2 = 2
我对预期输出的推理:
虽然 compute_sum 协程在 compute_product 协程之前被正确调用,但我的理解是一旦我们点击 await asyncio.sleep(5)
,控制将被传回事件循环,这将开始执行 compute_product 协程。为什么 "Returning sum" 在我们命中 compute_product 协程中的 print 语句之前被执行?
关于协程的工作原理,你是对的;您的问题在于您如何 呼叫 他们。特别是:
result_sum = await compute_sum(x, y)
这会调用协程 compute_sum
然后等待它完成。
因此,compute_sum
确实在 await asyncio.sleep(5)
中屈服于调度程序,但没有其他人可以唤醒。您的 print_computation
coro 已经在等待 compute_sum
。而且还没有人开始 compute_product
,所以它肯定不能 运行。
如果你想启动多个协程并同时运行,不要await
一个;你需要一起等待他们中的很多人。例如:
async def print_computation(x, y):
awaitable_sum = compute_sum(x, y)
awaitable_product = compute_product(x, y)
result_sum, result_product = await asyncio.gather(awaitable_sum, awaitable_product)
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
(awaitable_sum
是裸协程、Future
对象还是其他可以被 await
ed 的对象都无关紧要;gather
可以工作方式。)
或者,也许更简单:
async def print_computation(x, y):
result_sum, result_product = await asyncio.gather(
compute_sum(x, y), compute_product(x, y))
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
参见示例部分中的 Parallel execution of tasks。
这是它的工作原理。 让我们使用主线程作为主要参考...
主线程处理来自不同位置的事件和工作。如果其他线程同时触发了 3 个事件,则主线程一次只能处理一个。如果主线程正在处理您的循环,它将继续处理它,直到返回方法(或函数),然后再处理其他工作。
这意味着 'other work' 被放置在队列中,以便在主线程上成为 运行。
当您使用 'async await' 时,您会写 'async' 以告知该方法将(或可以)分解到它自己的一组队列中。然后当你说 'await' 它应该在另一个线程上工作。当它执行时,允许主线程处理存储在队列中的其他事件和工作,而不是仅仅在那里等待。
因此,当 await 工作完成时,它会将方法的剩余部分也放入主线程的队列中。
因此,在这些方法中,它不会继续处理,而是将剩余的工作放入等待完成时待完成的队列中。因此它是有序的。 await compute_sum(x, y)
将控制权交还给主线程以执行其他工作,当它完成后,其余的将添加到待处理的队列中。因此 await compute_product(x, y)
在前者完成后排队。
在 asyncio.gather()
does behind the scenes is that it wraps each coroutine in a Task
上展开,它表示正在后台完成的工作。
你可以把Task
对象当成Future
对象,代表一个可调用对象在不同线程中的执行,只是协程是不是对线程的抽象。
并且 ThreadPoolExecutor.submit(fn)
, a Task
can be created using asyncio.ensure_future(coro())
以同样的方式创建了 Future
个实例。
通过在等待之前将所有协程安排为任务,您的示例按预期工作:
async def print_computation(x, y):
task_sum = asyncio.ensure_future(compute_sum(x, y))
task_product = asyncio.ensure_future(compute_product(x, y))
result_sum = await task_sum
result_product = await task_product
print("%s + %s = %s" % (x, y, result_sum))
print("%s * %s = %s" % (x, y, result_product))
输出:
Compute 1 + 2 ...
Compute 1 x 2 ...
Returning product
Returning sum
1 + 2 = 3
1 * 2 = 2