【发布时间】:2020-01-08 23:30:34
【问题描述】:
既然 3.8 已经发布,我将再次尝试 asyncio 模块。但是,在尝试优雅地关闭事件循环时,我得到了意想不到的结果。具体来说,我正在监听SIGINT,取消正在运行的Tasks,收集那些Tasks,然后.stop()ing 事件循环。我知道Tasks 会在它们被取消时引发CancelledError,这将向上传播并结束我对asyncio.gather 的调用,除非根据documentation,我将return_exceptions=True 传递给asyncio.gather,这应该会导致gather 等待所有Tasks 取消并返回CancelledErrors 数组。但是,如果我尝试 gather 取消 Tasks,return_exceptions=True 似乎仍会导致我的 gather 呼叫立即中断。
这里是重现效果的代码。我正在运行 python 3.8.0:
# demo.py
import asyncio
import random
import signal
async def worker():
sleep_time = random.random() * 3
await asyncio.sleep(sleep_time)
print(f"Slept for {sleep_time} seconds")
async def dispatcher(queue):
while True:
await queue.get()
asyncio.create_task(worker())
tasks = asyncio.all_tasks()
print(f"Running Tasks: {len(tasks)}")
async def shutdown(loop):
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
for task in tasks:
task.cancel()
print(f"Cancelling {len(tasks)} outstanding tasks")
results = await asyncio.gather(*tasks, return_exceptions=True)
print(f"results: {results}")
loop.stop()
async def main():
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(shutdown(loop)))
queue = asyncio.Queue()
asyncio.create_task(dispatcher(queue))
while True:
await queue.put('tick')
await asyncio.sleep(1)
asyncio.run(main())
输出:
>> python demo.py
Running Tasks: 3
Slept for 0.3071352174511871 seconds
Running Tasks: 3
Running Tasks: 4
Slept for 0.4152310498820644 seconds
Running Tasks: 4
^CCancelling 4 outstanding tasks
Traceback (most recent call last):
File "demo.py", line 38, in <module>
asyncio.run(main())
File "/Users/max.taggart/.pyenv/versions/3.8.0/lib/python3.8/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/Users/max.taggart/.pyenv/versions/3.8.0/lib/python3.8/asyncio/base_events.py", line 608, in run_until_complete
return future.result()
asyncio.exceptions.CancelledError
我猜测事件循环仍有一些我不理解的地方,但我希望所有 CancelledErrors 都作为存储在 results 中的对象数组返回,然后能够继续而不是立即看到错误。
【问题讨论】:
标签: python python-asyncio