在使用 `loop.run_in_executor` 时,启动一个新的 `ThreadPoolExecutor` 有哪些优点和缺点?

编程语言 2026-07-11

在查看Event Loop的文档时,里面有这个例子:

import asyncio
import concurrent.futures

def blocking_io():
    # File operations (such as logging) can block the
    # event loop: run them in a thread pool.
    with open('/dev/urandom', 'rb') as f:
        return f.read(100)

def cpu_bound():
    # CPU-bound operations will block the event loop:
    # in general it is preferable to run them in a
    # process pool.
    return sum(i * i for i in range(10 ** 7))

async def main():
    loop = asyncio.get_running_loop()

    ## Options:

    # 1. Run in the default loop's executor:
    result = await loop.run_in_executor(
        None, blocking_io)
    print('default thread pool', result)

    # 2. Run in a custom thread pool:
    with concurrent.futures.ThreadPoolExecutor() as pool:
        result = await loop.run_in_executor(
            pool, blocking_io)
        print('custom thread pool', result)

    # 3. Run in a custom process pool:
    with concurrent.futures.ProcessPoolExecutor() as pool:
        result = await loop.run_in_executor(
            pool, cpu_bound)
        print('custom process pool', result)

    # 4. Run in a custom interpreter pool:
    with concurrent.futures.InterpreterPoolExecutor() as pool:
        result = await loop.run_in_executor(
            pool, cpu_bound)
        print('custom interpreter pool', result)

if __name__ == '__main__':
    asyncio.run(main())

假设是一个I/O密集型任务,相对于默认的 ThreadPoolExecutor,选项2 的优点是什么?是不是只是因为我可以控制它的初始化方式(num_workersinitializer 等),还是背后还有别的机制?

解决方案

只是因为我可以控制它的初始化方式(num_workers、initializer等)吗?

大体上,是的。它让你可以更好地调整线程数量等。特别需要注意的是,默认池中的线程数量在不同的Python版本之间有了很大变化,如文档所述(参见 the documentation)。因此,基于对某种行为的预期而依赖默认池,可能并不明智。

还有一些其他方面。例如,为不同的IO子系统使用不同的线程池可能更有意义,例如文件I/O、网络通信和数据库连接。这样不仅可以根据各子系统的性能和扩展性来调整并发操作数量,还能提高利用率。仅仅因为所有线程都在慢速网络连接上阻塞,并不意味着你在此期间不能完成文件I/O,对吗?

这也涉及到公平性。线程池按先到先服务的方式工作。如果一组任务是低优先级的后台操作,另一组是对用户可见的高优先级任务,将它们分成两个线程池意味着第一组不能仅因为更早被调度就垄断整个系统。并排运行两种任务所带来的额外系统负载仍然会有可衡量的影响,但至少高优先级的任务会有更早开始的机会。

最后一个方面是它允许更早地进行清理。如果你不需要线程池长期存在,可能只是用于初始化阶段或极少数操作,那么使用一个专门的线程池就意味着你完成后可以关闭它并释放相关的系统资源。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章