使用异步和线程

using asyncio and threads

在同一个 python 项目中同时使用 asyncio 和线程以使代码在不同的线程中运行是否有意义,其中一些 asyncio 用于获取异步活动的顺序查找代码?

或者尝试这样做是否意味着我缺少有关线程或异步使用的一些基本概念?

我不明白你在问什么(关于 "sequentially looking code for asynchronous activities" 的部分),但由于没有答案,我会写一些想法。

让我们谈谈为什么我们需要 asyncio/threads。假设我们有一个任务要发出两个请求。

  1. 如果我们将使用普通的单线程非异步代码,我们唯一的选择 是请求一个 url 并且只有在它完成之后 - 对于 另一个:

    request(url1)
    request(url2)
    

    这里的问题是我们做的工作效率低下:每个函数在执行的大部分时间里什么都不做,只是等待网络结果。如果我们能够以某种方式将 CPU 用于第二个请求,而第一个请求卡在网络内容中并且不需要它,那将会很酷。

  2. 这个问题可以通过运行不同线程中的函数来解决(并且通常可以解决):

    with ThreadPoolExecutor(max_workers=2) as e:
        e.submit(request, url1)
        e.submit(request, url2)
    

    这样我们会更快地得到结果。当第一个请求被网络卡住时,CPU 可以在另一个线程中为第二个请求做一些有用的事情。

    但这不是理想的解决方案:线程之间的切换有一些成本,执行流程比第一个示例更复杂。

    应该有更好的方法。

  3. 使用一个函数的空闲时间开始执行另一个函数就是asyncio的一般情况:

    await asyncio.gather(
        async_request(url1),
        async_request(url2),
    )
    

    事件循环管理执行流程:当第一个协程执行某些 I/O 操作并且 CPU 可用于在其他地方执行作业时,第二个协程启动。稍后的事件循环 returns 继续执行第一个协程。

    我们收到 "parallel" 请求并清理了可理解的代码。因为我们在单线程中有并行化,所以我们不需要另一个。

其实我们在使用asyncio线程的时候还是很有用的。如果我们愿意为他们付费,他们可以 help 我们将同步 I/O 函数转换为异步非常快:

async def async_request(url):
    loop = asyncio.get_event_loop()
    return (await loop.run_in_executor(None, request, url))

但同样,它是可选的,我们通常可以找到模块在没有线程的情况下异步发出请求(和其他 I/O 任务)。

当线程在异步程序中很有用时,我没有遇到任何其他任务。

当然这可能是有道理的。

异步代码原则上在同一个线程中运行一堆例程。

这意味着当一个例程必须等待输入或输出 (I/O) 时,它将暂时停止该例程并简单地开始处理另一个例程,直到它在那里遇到等待,等等。

多线程(或"parallelized"代码)原则上同时在您机器的不同内核上运行。 (请注意,在 Python 中,并行处理是通过使用下面@Yassine Faris 指出的多个进程实现的)。

在同一个程序中同时使用两者可能非常有意义。使用 asyncio 以便在等待 I/O 时继续处理。使用多线程(Python 中的多处理)在程序的另一部分并行执行大量计算。