什么时候不使用asyncio有意义?

时间:2019-04-22 11:07:40

标签: python multithreading asynchronous concurrent.futures

在什么情况下,一个线程或执行者(使用线程)在asyncio之上?

随着我使用Python(CPython)的经验的进步,它集中于优化工作中的脚本以批量执行某种形式的Web服务调用并处理响应。但是,经过几代脚本构建之后,我发现自己想知道为什么我不使用最新的脚本?

允许我在下面提供一些背景信息...

问题::从服务器A向客户端B请求N个文件,进行处理并将其保存到磁盘。

  1. 顺序

    • 构建请求容器,发送单个请求,处理响应,重复直到完成
    • 在直观上可以理解为“标准/初学者”方法
  2. 多线程

    • 重新构建容器,但同时发送多个请求
    • 使用信号量限制活动连接
    • 使用队列在工作人员之间共享和转储响应
    • 让主线程处理响应
    • 从本质上讲,工作人员是一劳永逸的,并且主要在循环中检查队列以获取数据
    • 将关注点与主要处理数据的主要对象隔离开
  3. ThreadPoolExecutor

    • 本质上与解决方案2相似,只是代码行少了
    • 理由:“我希望能够尽快处理回复”
    • 不需要显式实例化Queue和Semaphore
    • 如果没有记错,as_completed()中会使用Queue and Thread结构
    • 大致概述了here
  4. asyncio

    • 在这里引入了一个严重的混乱,但是概念大多数被理解了
    • 与解决方案2和3不同,它在单个线程上运行
    • 在实现中(非常)接近解决方案3,除了写入磁盘
    • 需要使用解决方案3组件才能通过run_in_executor()保存到磁盘

因此,我们陷入了当前的困境:为什么我要使用asyncio进行I / O绑定工作?

异步编程是一个非常类似于OOP的概念,解决方案3的文档甚至说“异步执行可以通过线程执行”。但是,如果我可以在单个线程上实现异步执行(不包括用于阻止对磁盘进行I / O的附加线程),为什么还要使用解决方案1-3?

我知道在GIL的情况下,CPython多线程是次优的;无论如何,我认为没有理由再有人使用线程或执行程序了。我做了很多谷歌搜索,看是否能找到一篇很好的文章,说明为什么人们更喜欢使用它们,但是我只发现了一些文章,说明为什么线程(以及随后使用线程的执行程序)很糟糕:上下文切换( GIL / OS),竞争条件,资源匮乏等...

由于CPython不使用线程来利用多个核心CPU(我相信是multiprocessing库),因此线程不用于繁重的计算任务;因此,将它们限制在I / O限制的操作中以提高性能。但是,那并没有给我足够的理由来理解为什么在asyncio上使用线程或执行程序。

如果您可以在单个线程中(可能是2-3个)完成所有操作,为什么还要继续引入创建,管理和销毁线程的开销(无论是显式的还是通过池/执行器的)?

1 个答案:

答案 0 :(得分:0)

我认为多线程和异步之间的决定实际上取决于您需要哪种多任务。如果一切都在程序的控制之下,那么异步/多处理可能一直都是正确的选择。但是,也许您想开始一项任务,在此前提下,多任务处理是正确的选择。例如,您在第三方库中启动任务。使用线程的一个显而易见的原因是该库不支持asyncio。但是,即使它支持异步,您也许也不想信任该库来随心所欲地控制您的任务。然后,您可以使用另一个运行该代码的asyncio事件循环来启动新线程。

所以我认为真正的问题是:什么时候使用合作社,什么时候使用抢占式多任务。