Python 3中的多处理与多线程与asyncio

multiprocessing vs multithreading vs asyncio in Python 3


问题

我发现在Python 3.4中,有几个不同的多处理/线程库: multiprocessing vs threading vs asyncio

但我不知道该用哪一个,或者说哪一个是 "推荐的"。它们是做同样的事情,还是有区别?如果是的话,哪一个是用来做什么的?我想写一个程序,在我的计算机中使用多核。但我不知道我应该学习哪个库。

也许 我太笨了,AsyncIO 有帮助
答案1

TL;DR

做出正确的选择:

我们已经走过了最流行的并发形式。但问题是--什么时候应该选择哪一种?这真的取决于用例。根据我的经验(和阅读),我倾向于遵循这个伪代码。

if io_bound:
    if io_very_slow:
        print("Use Asyncio")
    else:
        print("Use Threads")
else:
    print("Multi Processing")
  • CPU绑定=> 多处理
  • I/O绑定,快速I/O,有限的连接数=> 多线程
  • I/O绑定,慢I/O,许多连接=> Asyncio

参考


[ 注意 ] :

  • 如果你有一个长调用方法(i. 即一个包含睡眠时间或懒惰I/O的方法,最好的选择是 asyncio , Twisted Tornado 方法(coroutine方法),它以单线程作为并发工作。
  • asyncio Python3.4 及以后的版本上工作。
  • Tornado Twisted Python2.7开始准备就绪
  • uvloop 是超快的 asyncio 事件循环( uvloop 使 asyncio 速度提高2-4倍)。

[UPDATE (2019)]:

  • Japranto ( GitHub ) 是一个基于 uvloop 的非常快的管道化HTTP服务器。
所以,如果我有一个要请求的尿液列表,最好使用 Asyncio ?
@mingchau, 是的,但是请记住,当你使用从可等待函数时,你可以使用从 asynciorequest 库不是一个可等待的方法,相反,你可以使用如 aiohttp async-request
请在slowIO和fastIO上进行扩展,以实现多线程或异步>?
请你告诉我到底什么是io_very_slow
@变量I/O绑定意味着你的程序大部分时间都在与一个慢速设备对话,比如网络连接、硬盘、打印机,或者一个有睡眠时间的事件循环。因此,在阻塞模式下,你可以在线程或异步程序之间进行选择,如果你的边界部分非常慢,合作多任务(异步程序)是一个更好的选择(即避免到资源饥饿、死锁和竞赛条件)
答案2

它们的目的和/或要求(略有不同)。CPython(一个典型的、主线的Python实现)仍然有 全局解释器锁 ,所以多线程的应用(现在实现并行处理的标准方式)是次优的。这就是为什么 multiprocessing 可能 threading 更受欢迎。但并不是每个问题都可以有效地分割成[几乎独立的]片段,所以可能需要大量的进程间通信。这就是为什么 multiprocessing 在一般情况下可能不会比 threading 更受欢迎。

asyncio (这种技术不仅在Python中可用,其他语言和/或框架也有,例如 Boost.ASIO ) 是一种有效处理来自许多同时进行的I/O操作的方法,不需要并行代码执行。因此,它只是一个特定任务的解决方案(确实是一个好的解决方案!),而不是一般的并行处理。

注意到虽然这三者可能都没有实现并行化,但它们都能做并发(非阻塞)任务。
答案3

多处理 中,你利用多个CPU来分配你的计算。由于每个CPU都是并行运行的,你实际上能够同时运行多个任务。你会希望对 CPU绑定的 任务使用多处理技术。一个例子是试图计算一个巨大列表中所有元素的总和。如果你的机器有8个内核,你可以把列表 "切 "成8个小列表,在不同的内核上分别计算每个列表的总和,然后把这些数字相加。通过这样做,你会得到~8倍的速度。

在(多) 线程 中,你不需要多个CPU。想象一下,一个向网络发送大量HTTP请求的程序。如果你使用一个单线程程序,它将在每次请求时停止执行(block),等待响应,一旦收到响应就继续。这里的问题是,你的CPU在等待某个外部服务器完成工作时,并没有真正在做工作;它其实可以在这期间做一些有用的工作 解决的办法是使用线程--你可以创建许多线程,每个线程负责从网络上请求一些内容。线程的好处是,即使它们在一个CPU上运行,CPU也会不时地 "冻结 "一个线程的执行,并跳转到执行另一个线程(这被称为上下文切换,它以非确定性的间隔不断发生)。因此,如果你的任务是 I/O绑定 --使用线程。

asyncio 本质上是线程,其中 不是CPU,而是你,作为程序员(或者实际上是你的应用程序),决定哪里和何时发生上下文切换 。在Python中,你使用一个 await 关键字来暂停你的coroutine的执行(用 async 关键字定义)。

如果我有多个线程,然后我开始更快地得到响应--而在响应之后,我的工作更受CPU约束--我的进程会使用多核吗?也就是说,它是否会冻结线程,而不是同时使用多核?
不确定我是否理解了这个问题。它是关于当响应变得更快时,你是否应该使用多核?如果是这样的话--这取决于响应有多快,以及你真正花了多少时间来等待它们与使用CPU。如果你花大部分时间做CPU密集型的任务,那么分布在多个核心上会有好处(如果可能的话)。如果问题是系统在 "意识到 "其工作受CPU约束后是否会自发地切换到并行处理--我认为不会--通常你需要明确告诉它这样做。
我在想一个聊天工具的应用,在这个应用中,用户的聊天工具信息被发送到服务器上,而服务器使用POST请求将响应送回来?你认为这是否是一个CPU密集型的任务,因为发送和接收的响应可以是json,但我很怀疑--如果用户花时间输入他的响应会发生什么,这是否是一个慢I/O的例子?(用户延迟发送响应)
@TomaszBartkowiak 嗨,我有个问题。 我有一个实时的面部识别模型,它从一个网络摄像头接收输入,并显示用户是否在场。有一个明显的滞后,因为所有的帧都不是实时处理的,因为处理的速度比较慢。你能告诉我,如果我创建10个线程来处理10个帧,而不是在一个线程上处理这10个帧,多线程是否能帮助我? 澄清一下,我说的处理是指,在keras上有一个训练有素的模型,它把一个图像帧作为输入,并输出是否检测到人。
@TalalZahid 你的任务似乎是与CPU绑定的--只有机器(CPU)才会进行推理(检测),而不是等待IO或其他人来完成某些部分的工作(即调用外部API)。因此,做多线程就没有意义了。如果处理一个给定的帧需要相当长的时间(有吗?),而且每个帧都是独立的,那么你可以考虑将检测分布在不同的机器/核心上。
答案4

这是基本思路:

它是 IO -BOUND ? -----------> USE asyncio

IS IT CPU -HEAVY ? ---------> USE multiprocessing

ELSE ? ----------------------> USE threading

所以基本上坚持使用线程,除非你有IO/CPU问题。

答案5

已经有很多好的答案了。不能更详细地说明何时使用每一种。这更像是两个人的有趣组合。多处理+asyncio: https://pypi.org/project/aiomultiprocess/

设计它的用例是highio,但仍然利用尽可能多的可用内核。Facebook使用这个库来编写某种基于python的文件服务器。Asyncio允许IO绑定流量,但多处理允许多个事件循环和线程在多个核心上。

来自 repo 的 Ex 代码:

import asyncio
from aiohttp import request
from aiomultiprocess import Pool

async def get(url):
    async with request("GET", url) as response:
        return await response.text("utf-8")

async def main():
    urls = ["https://jreese.sh", ...]
    async with Pool() as pool:
        async for result in pool.map(get, urls):
            ...  # process result
            
if __name__ == '__main__':
    # Python 3.7
    asyncio.run(main())
    
    # Python 3.6
    # loop = asyncio.get_event_loop()
    # loop.run_until_complete(main())

这里只是一个补充,在jupyter笔记本中不能很好地工作,因为笔记本已经有一个Asyncio循环在运行。只是一个小小的说明,希望你不要把自己的头发拔掉。