Python 界有条不成文的准则: 计算密集型任务适合多进程,IO 密集型任务适合多线程。本篇来作个比较。

通常来说多线程相对于多进程有优势,因为创建一个进程开销比较大,然而因为在 python 中有 GIL 这把大锁的存在,导致执行计算密集型任务时多线程实际只能是单线程。而且由于线程之间切换的开销导致多线程往往比实际的单线程还要慢,所以在 python 中计算密集型任务通常使用多进程,因为各个进程有各自独立的 GIL,互不干扰。

而在 IO 密集型任务中,CPU 时常处于等待状态,操作系统需要频繁与外界环境进行交互,如读写文件,在网络间通信等。在这期间 GIL 会被释放,因而就可以使用真正的多线程。

以上是理论,下面做一个简单的模拟测试: 大量计算用 math.sin() + math.cos() 来代替,IO 密集型用 time.sleep() 来模拟。 在 Python 中有多种方式可以实现多进程和多线程,这里一并纳入看看是否有效率差异:

  1. 多进程: joblib.multiprocessing, multiprocessing.Pool, multiprocessing.apply_async, concurrent.futures.ProcessPoolExecutor
  2. 多线程: joblib.threading, threading.Thread, concurrent.futures.ThreadPoolExecutor
from multiprocessing import Pool
from threading import Thread
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time, os, math
from joblib import Parallel, delayed, parallel_backend def f_IO(a): # IO 密集型
time.sleep(5) def f_compute(a): # 计算密集型
for _ in range(int(1e7)):
math.sin(40) + math.cos(40)
return def normal(sub_f):
for i in range(6):
sub_f(i)
return def joblib_process(sub_f):
with parallel_backend("multiprocessing", n_jobs=6):
res = Parallel()(delayed(sub_f)(j) for j in range(6))
return def joblib_thread(sub_f):
with parallel_backend('threading', n_jobs=6):
res = Parallel()(delayed(sub_f)(j) for j in range(6))
return def mp(sub_f):
with Pool(processes=6) as p:
res = p.map(sub_f, list(range(6)))
return def asy(sub_f):
with Pool(processes=6) as p:
result = []
for j in range(6):
a = p.apply_async(sub_f, args=(j,))
result.append(a)
res = [j.get() for j in result] def thread(sub_f):
threads = []
for j in range(6):
t = Thread(target=sub_f, args=(j,))
threads.append(t)
t.start()
for t in threads:
t.join() def thread_pool(sub_f):
with ThreadPoolExecutor(max_workers=6) as executor:
res = [executor.submit(sub_f, j) for j in range(6)] def process_pool(sub_f):
with ProcessPoolExecutor(max_workers=6) as executor:
res = executor.map(sub_f, list(range(6))) def showtime(f, sub_f, name):
start_time = time.time()
f(sub_f)
print("{} time: {:.4f}s".format(name, time.time() - start_time)) def main(sub_f):
showtime(normal, sub_f, "normal")
print()
print("------ 多进程 ------")
showtime(joblib_process, sub_f, "joblib multiprocess")
showtime(mp, sub_f, "pool")
showtime(asy, sub_f, "async")
showtime(process_pool, sub_f, "process_pool")
print()
print("----- 多线程 -----")
showtime(joblib_thread, sub_f, "joblib thread")
showtime(thread, sub_f, "thread")
showtime(thread_pool, sub_f, "thread_pool") if __name__ == "__main__":
print("----- 计算密集型 -----")
sub_f = f_compute
main(sub_f)
print()
print("----- IO 密集型 -----")
sub_f = f_IO
main(sub_f)

结果:

----- 计算密集型 -----
normal time: 15.1212s ------ 多进程 ------
joblib multiprocess time: 8.2421s
pool time: 8.5439s
async time: 8.3229s
process_pool time: 8.1722s ----- 多线程 -----
joblib thread time: 21.5191s
thread time: 21.3865s
thread_pool time: 22.5104s ----- IO 密集型 -----
normal time: 30.0305s ------ 多进程 ------
joblib multiprocess time: 5.0345s
pool time: 5.0188s
async time: 5.0256s
process_pool time: 5.0263s ----- 多线程 -----
joblib thread time: 5.0142s
thread time: 5.0055s
thread_pool time: 5.0064s

上面每一方法都统一创建6个进程/线程,结果是计算密集型任务中速度:多进程 > 单进程/线程 > 多线程, IO 密集型任务速度: 多线程 > 多进程 > 单进程/线程。

/

Python 多进程、多线程效率比较的更多相关文章

  1. Python 多进程 多线程 协程 I/O多路复用

    引言 在学习Python多进程.多线程之前,先脑补一下如下场景: 说有这么一道题:小红烧水需要10分钟,拖地需要5分钟,洗菜需要5分钟,如果一样一样去干,就是简单的加法,全部做完,需要20分钟:但是, ...

  2. python 多进程/多线程/协程 同步异步

    这篇主要是对概念的理解: 1.异步和多线程区别:二者不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段.异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事 ...

  3. python 多进程多线程的对比

    link:http://www.cnblogs.com/whatisfantasy/p/6440585.html mark一下,挺详细

  4. Python的多线程和多进程

    (1)多线程的产生并不是因为发明了多核CPU甚至现在有多个CPU+多核的硬件,也不是因为多线程CPU运行效率比单线程高.单从CPU的运行效率上考虑,单任务进程及单线程效率是最高的,因为CPU没有任何进 ...

  5. Python中多线程与多进程的恩恩怨怨

    概念: 并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运 ...

  6. Python中单线程、多线程和多进程的效率对比实验

    GIL机制导致如下结果: Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)python多线程适合io操作密集型的任务(如sock ...

  7. Python的多线程(threading)与多进程(multiprocessing )

    进程:程序的一次执行(程序载入内存,系统分配资源运行).每个进程有自己的内存空间,数据栈等,进程之间可以进行通讯,但是不能共享信息. 线程:所有的线程运行在同一个进程中,共享相同的运行环境.每个独立的 ...

  8. python数据采集与多线程效率分析

    以前一直使用PHP写爬虫,用Snoopy配合simple_html_dom用起来也挺好的,至少能够解决问题. PHP一直没有一个好用的多线程机制,虽然可以使用一些trick的手段来实现并行的效果(例如 ...

  9. python采用 多进程/多线程/协程 写爬虫以及性能对比,牛逼的分分钟就将一个网站爬下来!

    首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务.一个CPU,在一个时间切片里只能运行一个程序. 从操作系统的角度: 进程和线程,都 ...

  10. python 多进程开发与多线程开发

    转自: http://tchuairen.blog.51cto.com/3848118/1720965 博文作者参考的博文:  博文1  博文2 我们先来了解什么是进程? 程序并不能单独运行,只有将程 ...

随机推荐

  1. hdu 6406 Taotao Picks Apples 线段树 单点更新

    Taotao Picks Apples Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Ot ...

  2. # WinForm关闭窗体确认

    private void Lba_IE_Form_FormClosing(object sender, FormClosingEventArgs e) { if (MessageBox.Show(&q ...

  3. Codeforces Round #323 (Div. 2) D. Once Again... 乱搞+LIS

    D. Once Again... time limit per test 1 second memory limit per test 256 megabytes input standard inp ...

  4. Miller_Rabin(米勒拉宾)素数测试算法

    首先需要知道两个定理: 1: 费马小定理: 假如p是素数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p). 2:二次探测定理:如果p是素数,x是小于p的正整数,且,那么要么x=1,要么x ...

  5. python 元组中元组转为字典

    #create a tuple tuplex = ((, , "r")) print(dict((y, x) for x, y in tuplex))

  6. SQL 常用的命令

    --修改表名 --格式:SP_RENAME TABLENAME,NEWTABLENAME SP_RENAME TABLENAME,NEWTABLENAME --只能对表,不能对临时表 --修改字段名 ...

  7. Beta冲刺三——《WAP团队》

    β冲刺第三天  1. 今日完成任务情况以及遇到的问题. ①马麒.杜有海:管理员审核表的进一步完善 ②郝明宇:登录.注册界面的完善 ③马宏伟.周欣:继续完善前端数据借用与后台的连接 ④乌勒扎:登录与注册 ...

  8. axios构建请求池处理全局loading状态&&axios避免重复请求

    很多时候我们能够看到类似进度条一样的东西在页面顶部进行加载,代表页面是否加载完成,或者其他的loading效果,我们当然不可能通过promise.all来讲所有的请求合并到一起然后进行处理,这个时候我 ...

  9. HDU-2829 Lawrence (DP+四边形不等式优化)

    题目大意:有n个敌方军火库呈直线排列,每个军火库有一个值vi,并且任意相邻的两个库之间都有通道相连.对于任意一条连起来的军火库链,它对我方的威胁可以用函数w(i,j)表示为:w(i,j)=vi*sum ...

  10. AIX的iostat命令解析(翻译红皮书)

    1.确定磁盘使用率 $ iostat -T 2 10System configuration: lcpu=8 drives=29 paths=52 vdisks=0tty:      tin      ...