如何正确使用run_in_executor?

时间:2019-05-22 08:26:34

标签: python python-asyncio

我尝试使用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,其中executorNoneasync_task的执行与cpu_bound的执行同时进行。

在其他情况下,async_task将在完成cpu_bound之后执行。 我认为当我们使用ProcessPoolExecutor任务时,不应阻塞循环。我在哪里错了?

1 个答案:

答案 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)之类的值等待它们。这样不仅可以收集结果,还可以确保将线程外函数中发生的异常正确地传播到您的代码中,而不是丢弃。