我发现在Python 3.4中,有几个不同的多处理/线程库: multiprocessing vs threading vs asyncio 。
但我不知道该用哪一个,或者说哪一个是 "推荐的"。它们是做同样的事情,还是有区别?如果是的话,哪一个是用来做什么的?我想写一个程序,在我的计算机中使用多核。但我不知道我应该学习哪个库。
我们已经走过了最流行的并发形式。但问题是--什么时候应该选择哪一种?这真的取决于用例。根据我的经验(和阅读),我倾向于遵循这个伪代码。
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
[ 注意 ] :
asyncio
事件循环( uvloop 使 asyncio
速度提高2-4倍)。 [UPDATE (2019)]:
asyncio
, request
库不是一个可等待的方法,相反,你可以使用如 aiohttp
库 或 async-request 等
它们的目的和/或要求(略有不同)。CPython(一个典型的、主线的Python实现)仍然有 全局解释器锁 ,所以多线程的应用(现在实现并行处理的标准方式)是次优的。这就是为什么 multiprocessing
可能 比 threading
更受欢迎。但并不是每个问题都可以有效地分割成[几乎独立的]片段,所以可能需要大量的进程间通信。这就是为什么 multiprocessing
在一般情况下可能不会比 threading
更受欢迎。
asyncio
(这种技术不仅在Python中可用,其他语言和/或框架也有,例如 Boost.ASIO ) 是一种有效处理来自许多同时进行的I/O操作的方法,不需要并行代码执行。因此,它只是一个特定任务的解决方案(确实是一个好的解决方案!),而不是一般的并行处理。
在 多处理 中,你利用多个CPU来分配你的计算。由于每个CPU都是并行运行的,你实际上能够同时运行多个任务。你会希望对 CPU绑定的 任务使用多处理技术。一个例子是试图计算一个巨大列表中所有元素的总和。如果你的机器有8个内核,你可以把列表 "切 "成8个小列表,在不同的内核上分别计算每个列表的总和,然后把这些数字相加。通过这样做,你会得到~8倍的速度。
在(多) 线程 中,你不需要多个CPU。想象一下,一个向网络发送大量HTTP请求的程序。如果你使用一个单线程程序,它将在每次请求时停止执行(block),等待响应,一旦收到响应就继续。这里的问题是,你的CPU在等待某个外部服务器完成工作时,并没有真正在做工作;它其实可以在这期间做一些有用的工作 解决的办法是使用线程--你可以创建许多线程,每个线程负责从网络上请求一些内容。线程的好处是,即使它们在一个CPU上运行,CPU也会不时地 "冻结 "一个线程的执行,并跳转到执行另一个线程(这被称为上下文切换,它以非确定性的间隔不断发生)。因此,如果你的任务是 I/O绑定 --使用线程。
asyncio 本质上是线程,其中 不是CPU,而是你,作为程序员(或者实际上是你的应用程序),决定哪里和何时发生上下文切换 。在Python中,你使用一个 await
关键字来暂停你的coroutine的执行(用 async
关键字定义)。
这是基本思路:
它是 IO -BOUND ? -----------> USE
asyncio
IS IT CPU -HEAVY ? ---------> USE
multiprocessing
ELSE ? ----------------------> USE
threading
所以基本上坚持使用线程,除非你有IO/CPU问题。
已经有很多好的答案了。不能更详细地说明何时使用每一种。这更像是两个人的有趣组合。多处理+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循环在运行。只是一个小小的说明,希望你不要把自己的头发拔掉。