我发现在Python 3.4中,有几个不同的多处理/线程库:multiprocessing vs threading vs asyncio。
但我不知道该用哪一个,或者是“推荐的”。它们做的事情是一样的,还是不同的?如果是的话,哪个是用来干什么的?我想在我的计算机上写一个使用多核的程序。但我不知道该学哪个图书馆。
我发现在Python 3.4中,有几个不同的多处理/线程库:multiprocessing vs threading vs asyncio。
但我不知道该用哪一个,或者是“推荐的”。它们做的事情是一样的,还是不同的?如果是的话,哪个是用来干什么的?我想在我的计算机上写一个使用多核的程序。但我不知道该学哪个图书馆。
当前回答
多处理可以并行运行。 多线程和asyncio不能并行运行。
使用英特尔(R)酷睿(TM) i7-8700K CPU @ 3.70GHz和32.0 GB RAM,我用2个进程、2个线程和2个异步任务计算了2到100000之间有多少素数,如下所示。*这是CPU限制计算:
Multiprocessing | Multithreading | asyncio |
---|---|---|
23.87 seconds | 45.24 seconds | 44.77 seconds |
因为多处理可以并行运行,所以如上所示,多处理比多线程和asyncio快两倍。
我使用了以下3组代码:
多处理:
# "process_test.py"
from multiprocessing import Process
import time
start_time = time.time()
def test():
num = 100000
primes = 0
for i in range(2, num + 1):
for j in range(2, i):
if i % j == 0:
break
else:
primes += 1
print(primes)
if __name__ == "__main__": # This is needed to run processes on Windows
process_list = []
for _ in range(0, 2): # 2 processes
process = Process(target=test)
process_list.append(process)
for process in process_list:
process.start()
for process in process_list:
process.join()
print(round((time.time() - start_time), 2), "seconds") # 23.87 seconds
结果:
...
9592
9592
23.87 seconds
多线程:
# "thread_test.py"
from threading import Thread
import time
start_time = time.time()
def test():
num = 100000
primes = 0
for i in range(2, num + 1):
for j in range(2, i):
if i % j == 0:
break
else:
primes += 1
print(primes)
thread_list = []
for _ in range(0, 2): # 2 threads
thread = Thread(target=test)
thread_list.append(thread)
for thread in thread_list:
thread.start()
for thread in thread_list:
thread.join()
print(round((time.time() - start_time), 2), "seconds") # 45.24 seconds
结果:
...
9592
9592
45.24 seconds
Asyncio:
# "asyncio_test.py"
import asyncio
import time
start_time = time.time()
async def test():
num = 100000
primes = 0
for i in range(2, num + 1):
for j in range(2, i):
if i % j == 0:
break
else:
primes += 1
print(primes)
async def call_tests():
tasks = []
for _ in range(0, 2): # 2 asyncio tasks
tasks.append(test())
await asyncio.gather(*tasks)
asyncio.run(call_tests())
print(round((time.time() - start_time), 2), "seconds") # 44.77 seconds
结果:
...
9592
9592
44.77 seconds
其他回答
已经有很多好的答案了。无法详细说明何时使用每种方法。这更像是两者的有趣结合。Multiprocessing + asyncio: https://pypi.org/project/aiomultiprocess/。
它的设计用例是高容量的,但仍然使用尽可能多的可用内核。Facebook使用这个库来编写某种基于python的文件服务器。Asyncio允许IO绑定流量,但multiprocessing允许多个事件循环和多个内核上的线程。
回购中的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循环运行。只是给你留个小纸条,别扯头发。
多处理可以并行运行。 多线程和asyncio不能并行运行。
使用英特尔(R)酷睿(TM) i7-8700K CPU @ 3.70GHz和32.0 GB RAM,我用2个进程、2个线程和2个异步任务计算了2到100000之间有多少素数,如下所示。*这是CPU限制计算:
Multiprocessing | Multithreading | asyncio |
---|---|---|
23.87 seconds | 45.24 seconds | 44.77 seconds |
因为多处理可以并行运行,所以如上所示,多处理比多线程和asyncio快两倍。
我使用了以下3组代码:
多处理:
# "process_test.py"
from multiprocessing import Process
import time
start_time = time.time()
def test():
num = 100000
primes = 0
for i in range(2, num + 1):
for j in range(2, i):
if i % j == 0:
break
else:
primes += 1
print(primes)
if __name__ == "__main__": # This is needed to run processes on Windows
process_list = []
for _ in range(0, 2): # 2 processes
process = Process(target=test)
process_list.append(process)
for process in process_list:
process.start()
for process in process_list:
process.join()
print(round((time.time() - start_time), 2), "seconds") # 23.87 seconds
结果:
...
9592
9592
23.87 seconds
多线程:
# "thread_test.py"
from threading import Thread
import time
start_time = time.time()
def test():
num = 100000
primes = 0
for i in range(2, num + 1):
for j in range(2, i):
if i % j == 0:
break
else:
primes += 1
print(primes)
thread_list = []
for _ in range(0, 2): # 2 threads
thread = Thread(target=test)
thread_list.append(thread)
for thread in thread_list:
thread.start()
for thread in thread_list:
thread.join()
print(round((time.time() - start_time), 2), "seconds") # 45.24 seconds
结果:
...
9592
9592
45.24 seconds
Asyncio:
# "asyncio_test.py"
import asyncio
import time
start_time = time.time()
async def test():
num = 100000
primes = 0
for i in range(2, num + 1):
for j in range(2, i):
if i % j == 0:
break
else:
primes += 1
print(primes)
async def call_tests():
tasks = []
for _ in range(0, 2): # 2 asyncio tasks
tasks.append(test())
await asyncio.gather(*tasks)
asyncio.run(call_tests())
print(round((time.time() - start_time), 2), "seconds") # 44.77 seconds
结果:
...
9592
9592
44.77 seconds
这是基本思想:
是IO-BOUND吗?----------->使用asyncio 它的cpu量大吗?--------->使用多处理 其他的吗?---------------------->使用线程
所以基本上坚持线程,除非你有IO/CPU问题。
博士TL;
做出正确的选择:
我们已经介绍了最流行的并发形式。但问题依然存在——什么时候应该选择哪一个?这实际上取决于用例。根据我的经验(和阅读),我倾向于遵循以下伪代码:
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/O的方法),最好的选择是asyncio, Twisted或Tornado方法(协程方法),它与单个线程一起工作作为并发。 asyncio适用于Python3.4及更高版本。 Tornado和Twisted从Python2.7开始就准备好了 Uvloop是超快的asyncio事件循环(Uvloop使asyncio快2-4倍)。
(更新(2019)):
Japranto (GitHub)是一个非常快速的基于uvloop的流水线HTTP服务器。
多处理 每个进程都有自己的Python解释器,并且可以在处理器的独立内核上运行。Python multiprocessing是一个包,它支持使用类似于threading模块的API生成进程。多处理包提供了真正的并行性,通过使用子进程而不是线程,有效地避开了全局解释器锁。
当你有CPU密集型任务时,使用多处理。
多线程 Python多线程允许在进程中生成多个线程。这些线程可以共享进程的相同内存和资源。在CPython中,由于全局解释器锁,在任何给定的时间都只能运行一个线程,因此你不能利用多个内核。由于GIL的限制,Python中的多线程并不能提供真正的并行性。
Asyncio Asyncio致力于协作多任务概念。Asyncio任务运行在同一个线程上,因此没有并行性,但它为开发人员提供了更好的控制,而不是操作系统,这是多线程的情况。
关于asyncio相对于线程的优点,在这个链接上有一个很好的讨论。
Python摘要中的多处理VS线程VS AsyncIO