我尝试使用run_in_executor
并有一些疑问。这是代码(基本上是从文档复制的代码)
import asyncio
import concurrent.futures
def cpu_bound(val):
# CPU-bound operations will block the event loop:
# in general it is preferable to run them in a
# process pool.
print(f'Start task: {val}')
sum(i * i for i in range(10 ** 7))
print(f'End task: {val}')
async def async_task(val):
print(f'Start async task: {val}')
while True:
print(f'Tick: {val}')
await asyncio.sleep(1)
async def main():
loop = asyncio.get_running_loop()
## Options:
for i in range(5):
loop.create_task(async_task(i))
# 1. Run in the default loop's executor:
# for i in range(10):
# loop.run_in_executor(
# None, cpu_bound, i)
# print('default thread pool')
# 2. Run in a custom thread pool:
# with concurrent.futures.ThreadPoolExecutor(max_workers=10) as pool:
# for i in range(10):
# loop.run_in_executor(
# pool, cpu_bound, i)
# print('custom thread pool')
# 3. Run in a custom process pool:
with concurrent.futures.ProcessPoolExecutor(max_workers = 10) as pool:
for i in range(10):
loop.run_in_executor(
pool, cpu_bound, i)
print('custom process pool')
while True:
await asyncio.sleep(1)
asyncio.run(main())
情况1:run_in_executor
,其中executor
是None
:
async_task
的执行与cpu_bound
的执行同时进行。
在其他情况下,async_task
将在完成cpu_bound
之后执行。
我认为当我们使用ProcessPoolExecutor
任务时,不应阻塞循环。我在哪里错了?
答案 0 :(得分:0)
在其他情况下,
async_task
将在完成cpu_bound
之后执行。我认为当我们使用ProcessPoolExecutor
任务时,不应阻塞循环。我在哪里错了?
问题在于with XXXPoolExecutor()
在with
块的末尾关闭了池。池关闭等待挂起的任务完成,这会阻塞事件循环,并且与asyncio不兼容。由于您的第一个变体不涉及with
语句,因此它没有此问题。
解决方案是简单地删除with
语句并创建一次池(例如在顶级或main()
中),然后在 use 中使用它。功能。如果需要,您可以在pool.shutdown()
完成后通过调用asyncio.run()
显式关闭池。
还要注意,您永远不会等待 loop.run_in_executor
返回的期货。这是一个错误,异步可能会警告您。您可能应该将返回的值收集在一个列表中,并用results = await asyncio.gather(*tasks)
之类的值等待它们。这样不仅可以收集结果,还可以确保将线程外函数中发生的异常正确地传播到您的代码中,而不是丢弃。