[英]Python async-generator not async
我的代碼如下。 我希望兩個睡眠可以共享相同的時間幀,並運行腳本1 + 2 * 3 = 7秒。 但似乎發生了一些錯誤,所以它仍然需要3 *(1 + 2)秒。
有沒有想法如何修改代碼?
import asyncio
async def g():
for i in range(3):
await asyncio.sleep(1)
yield i
async def main():
async for x in g():
print(x)
await asyncio.sleep(2)
loop = asyncio.get_event_loop()
res = loop.run_until_complete(main())
loop.close()
async
/ await
的要點是交錯任務 ,而不是函數/生成器。 例如,當您await asyncio.sleep(1)
,您當前的協程會隨着睡眠而延遲。 同樣, async for
延遲其協程,直到下一個項目准備就緒。
要運行單獨的功能,必須將每個部件創建為單獨的任務。 使用Queue
在它們之間交換項目 - 任務只會延遲到交換項目為止。
from asyncio import Queue, sleep, run, gather
# the original async generator
async def g():
for i in range(3):
await sleep(1)
yield i
async def producer(queue: Queue):
async for i in g():
print('send', i)
await queue.put(i) # resume once item is fetched
await queue.put(None)
async def consumer(queue: Queue):
x = await queue.get() # resume once item is fetched
while x is not None:
print('got', x)
await sleep(2)
x = await queue.get()
async def main():
queue = Queue()
# tasks only share the queue
await gather(
producer(queue),
consumer(queue),
)
run(main())
如果您經常需要此功能,還可以將其放入包裝異步迭代的輔助對象中。 幫助程序封裝了隊列和單獨的任務。 您可以直接在async for
語句中的異步迭代中應用幫助程序。
from asyncio import Queue, sleep, run, ensure_future
# helper to consume iterable as concurrent task
async def _enqueue_items(async_iterable, queue: Queue, sentinel):
async for item in async_iterable:
await queue.put(item)
await queue.put(sentinel)
async def concurrent(async_iterable):
"""Concurrently fetch items from ``async_iterable``"""
queue = Queue()
sentinel = object()
consumer = ensure_future( # concurrently fetch items for the iterable
_enqueue_items(async_iterable, queue, sentinel)
)
try:
item = await queue.get()
while item is not sentinel:
yield item
item = await queue.get()
finally:
consumer.cancel()
# the original generator
async def g():
for i in range(3):
await sleep(1)
yield i
# the original main - modified with `concurrent`
async def main():
async for x in concurrent(g()):
print(x)
await sleep(2)
run(main())
作為使用Queue執行此操作的替代方法,而是將此解決方案鏈連接在一起,以便Future的結果是當前項目和另一個Future來檢索下一個項目(有點像鏈接列表,可以這么說):
from asyncio import sleep, get_event_loop, run, create_task
async def aiter(fut, async_generator):
try:
async for item in async_generator:
fut, prev_fut = get_event_loop().create_future(), fut
prev_fut.set_result((item, fut))
else:
fut.set_exception(StopAsyncIteration())
except Exception as e:
fut.set_exception(e)
async def concurrent(async_generator):
fut = get_event_loop().create_future()
create_task(aiter(fut, async_generator))
try:
while True:
item, fut = await fut
yield item
except StopAsyncIteration as e:
return
作為一個額外的好處,這個解決方案將通過在main()方法中使用對調試有用的回溯來重新引用異常來正確處理g()中發生的異常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.