通过线程+队列或ThreadPoolExecutor使用异步等待吗? [英] Using async-await over Thread + Queue or ThreadPoolExecutor?

查看:298
本文介绍了通过线程+队列或ThreadPoolExecutor使用异步等待吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从没使用过async-await语法,但是在等待将来的响应时,我确实经常需要发出HTTP/S请求并解析响应.为了完成此任务,我目前使用 ThreadPoolExecutor 类,它们始终以异步方式执行调用;有效地,我可以达到(我相信)使用更多代码行来使用async-await所获得的相同结果.

I've never used the async-await syntax but I do often need to make HTTP/S requests and parse responses while awaiting future responses. To accomplish this task, I currently use the ThreadPoolExecutor class which execute the calls asynchronously anyways; effectively I'm achieving (I believe) the same result I would get with more lines of code to use async-await.

在假设我当前的实现异步工作的情况下进行操作,我想知道async-await实现与我最初使用线程和队列管理工作程序的实现有何不同?它还使用信号量来限制工作人员.

Operating under the assumption that my current implementations work asynchronously, I am wondering how the async-await implementation would differ from that of my original one which used Threads and a Queue to manage workers; it also used a Semaphore to limit workers.

该实现是在以下条件下设计的:

That implementation was devised under the following conditions:

  • 可能有任意数量的请求
  • 活动请求总数可能为4
  • 仅在收到响应时发送下一个请求

实施的基本流程如下:

  1. 生成请求容器
  2. 创建一个ListeningQueue
  3. 为每个请求创建一个线程并传递URL,ListeningQueue和Semaphore
  4. 每个线程尝试获取信号量(限制为4个线程)
  5. 主线程在while中继续检查ListeningQueue
  6. 当线程收到响应时,将其放在ListeningQueue中并释放Semaphore
  7. 等待的线程获取信号量(过程重复)
  8. 主线程处理响应,直到计数等于请求数
  1. Generate container of requests
  2. Create a ListeningQueue
  3. For each request create a Thread and pass the URL, ListeningQueue and Semaphore
  4. Each Thread attempts to acquire the Semaphore (limited to 4 Threads)
  5. Main Thread continues in a while checking ListeningQueue
  6. When a Thread receives a response, place in ListeningQueue and release Semaphore
  7. A waiting Thread acquires Semaphore (process repeats)
  8. Main Thread processes responses until count equals number of requests

因为我需要限制使用信号量的活动线程的数量,并且如果要使用async-await进行尝试,则必须在主线程或async def中设计一些阻止请求的逻辑如果已达到限制,则从发送.除了该约束,我看不出在哪里使用async-await会更有用.是否通过消除线程来降低开销和竞争条件的机会?那是主要好处吗?如果是这样,即使使用ThreadPoolExecutor进行异步调用,它仍使用线程池,从而使async-await成为更好的选择吗?

Because I need to limit the number of active Threads I use a Semaphore, and if I were to try this using async-await I would have to devise some logic in the Main Thread or in the async def that prevents a request from being sent if the limit has been reached. Apart from that constraint, I don't see where using async-await would be any more useful. Is it that it lowers overhead and race condition chances by eliminating Threads? Is that the main benefit? If so, even though using a ThreadPoolExecutor is making asynchronous calls it is using a pool of Threads, thus making async-await a better option?

推荐答案

假设当前的实现异步运行,我想知道async-await实现与我最初使用Threads和Queue管理工作程序的实现有何不同

Operating under the assumption that my current implementations work asynchronously, I am wondering how the async-await implementation would differ from that of my original one which used Threads and a Queue to manage workers

使用 asyncio 和async-await,具有自己的信号灯的使用方式大致相同.请参阅此问题的答案,以获取使用固定数量的任务或使用信号量限制并行请求数量的示例.

It would not be hard to implement very similar logic using asyncio and async-await, which has its own version of semaphore that is used in much the same way. See answers to this question for examples of limiting the number of parallel requests with a fixed number of tasks or by using a semaphore.

关于asyncio与使用线程的等效代码相比的优势,有几个:

As for advantages of asyncio over equivalent code using threads, there are several:

  • 所有内容都在单个线程中运行,而不管活动连接的数量如何.您的程序可以扩展到大量并发任务,而不会因为线程数量不合理而淹没了操作系统,也不必等待线程池中的空闲插槽才能开始下载.

  • Everything runs in a single thread regardless of the number of active connections. Your program can scale to a large number of concurrent tasks without swamping the OS with an unreasonable number of threads or the downloads having to wait for a free slot in the thread pool before they even start.

正如您所指出的,单线程执行不太容易出现竞争条件,因为可以用await清楚地标记了可能发生任务切换的点,并且它们之间的所有内容实际上都是原子的.这种优点在小型线程程序中不太明显,在小型线程程序中,执行程序只是以即发即发的方式将任务移交给线程,但是随着逻辑变得越来越复杂,线程开始共享更多状态(例如,由于缓存或某些原因)同步逻辑),这变得更加明显.

As you pointed out, single-threaded execution is less susceptible to race conditions because the points where a task switch can occur are clearly marked with await, and everything in-between is effectively atomic. The advantage of this is less obvious in small threaded programs where the executor just hands tasks to threads in a fire-and-collect fashion, but as the logic grows more complex and the threads begin to share more state (e.g. due to caching or some synchronization logic), this becomes more pronounced.

异步/等待使您可以轻松地为监视,日志记录和清除之类的操作创建其他独立任务.使用线程时,那些线程不适合执行程序模型,并需要其他线程,并且总是带有暗示线程被滥用的设计气味.使用asyncio,每个任务都可以像在自己的线程中运行一样,并使用await等待某些事情发生(并控制其他任务)-例如基于计时器的监视任务将由等待 asyncio.sleep() ,但是逻辑可以任意复杂.尽管代码看起来是顺序的,但每个任务都是轻量级的,并且对操作系统的权重不比分配的小对象大.

async/await allows you to easily create additional independent tasks for things like monitoring, logging and cleanup. When using threads, those do not fit the executor model and require additional threads, always with a design smell that suggests threads are being abused. With asyncio, each task can be as if it were running in its own thread, and use await to wait for something to happen (and yield control to others) - e.g. a timer-based monitoring task would consist of a loop that awaits asyncio.sleep(), but the logic could be arbitrarily complex. Despite the code looking sequential, each task is lightweight and carries no more weight to the OS than that of a small allocated object.

async/await支持可靠的取消,线程从未执行过并且很可能不会执行.这通常被忽略,但是在asyncio中,完全有可能取消一个正在运行的任务,这会导致它从await唤醒,但有一个终止该任务的异常.取消使使用超时,任务组和其他模式变得简单明了,而这些模式在使用线程时是不可能的或繁琐的工作.

async/await supports reliable cancellation, which threads never did and likely never will. This is often overlooked, but in asyncio it is perfectly possible to cancel a running task, which causes it to wake up from await with an exception that terminates it. Cancellation makes it straightforward to implement timeouts, task groups, and other patterns that are impossible or a huge chore when using threads.

另一方面,异步/等待的缺点是所有代码都必须异步.除其他外,这意味着您不能使用诸如请求之类的库,而必须切换到诸如 aiohttp .

On the flip side, the disadvantage of async/await is that all your code must be async. Among other things, it means that you cannot use libraries like requests, you have to switch to asyncio-aware alternatives like aiohttp.

这篇关于通过线程+队列或ThreadPoolExecutor使用异步等待吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆