Python 多线程进程高级指南(二)
本文是如何《优雅地实现Python通用多线程/进程并行模块》的后续。因为我发现,自认为懂了一点多线程开发的皮毛,写了那么个multi_helper的玩意儿,后来才发现我靠原来就是一坨屎。自己辛苦开发的并行库,在Python的原生类库中就有了优雅地多的实现。并且还有更优雅的asyncio库!这简直让人累觉不爱。
首先,并行和并发是不同的。100个进程可以在1个CPU上并发地执行,但却能在4个CPU上并行地执行。这就能看出两者的不同。
在实际编程中,有大量的计算或IO是一下子无法结束的。那么如何充分利用各种资源呢?有两个流派:
- 多进程,多线程(并行)
- 协程 (并发)
本文实在不能算完整,但汇集了笔者觉得很精髓的不少内容,因此可以认为是一篇综述性文章。
多线程和多进程
先说前者,定义本身是常识就不多说。初级程序员为了让程序变快,于是就狂开线程,不论是IO操作还是计算,能开100个就绝不开50个。线程回收一律不管,这程序只能是玩具。
不过,众所周知Python有GIL(全局解释器锁),任何时候都只有一个线程在工作。那多线程加速还玩个屁啊?!龟叔站出来了,他说:
- Python在IO操作时,会自动释放GIL,因此IO密集型程序的多线程是有意义的
- 即使不能多线程,还能多进程,这才是核武器。而Python有非常好用的multiprocessing库解决进程间协同问题。
然而,这些耗时任务,如果不需要知道结果,那还好说(很多程序员直接就把结果写到不同的文件里去,从而避免此问题,太low了)。可是每次都写文件再读出来这太麻烦了。如何收集计算结果呢?
笔者那个工具的做法,是将结果保存到一个队列中,从外部对其进行消费。但Python官方库早就有类似的实现了,多线程和多进程版本分别叫ThreadPoolExecutor
和ProcessPoolExecutor
。至于怎么用,出门问度娘。老司机在文章里贴代码太没有逼格了。
好,你度娘上完了。我们会发现他们都在concurrent.futures
库里。这个表述很有意思。这是未来才能知道结果的任务,因此叫future
。 在《Python高手之路》的第14章里,这样的概念叫期物。你如果你要获取某个任务的result,就必须等待该任务完成。也可以创建任务的数组,然后批量去执行它。类似的还有C#的async关键字以及task的result属性。
让我觉得比较神奇的是,Python的python-parallelize
库,就能隐含地将for循环转换为并行for循环,代码更加简洁。这才是多进程的终极神器。写法如下:
import os
from parallelize import parallelize
for i in parallelize(range(100)):
print(os.getpid(), i)
你要做的,仅仅是在迭代器外面套上套子,其他并行的操作就都由它执行完了。你猜这个库的核心代码有多少行?30行不到!它用os.fork创建出很多子进程,之后将不同的任务分配到这些进程上。而代码编写和串行程序几乎没有区别。
对多数人来说,高阶函数本来就比较反人类。这样写下来反而会清爽很多。值得学习!这部分写完了,我们进入协程的神秘世界。
asyncio 和 yield from
协程(coroutine)是比线程更轻量级的概念。大家会好奇协程的本质,因为当年学C语言时可是完全没这个东西啊。协程说白了就是语法糖。编译器会把协程的一坨东西整合到大的状态机里,类似于一堆switch-case和while的代码里,从而实现无缝调度。但对代码编写者来说,同一语义的依然能在同一个代码位置,从而更好理解。
yield是Python中的关键字。我们熟悉的是它的迭代生成器用法。更高阶的则是协程。例如:
a=yield b
如果你认为a=b,那就大错特错了。a是外部send函数的返回值。至于更详细的信息,可参考
到了Python3, 更丧心病狂的yield from横空出世。最浅显的理解,是下面的代码:
def merge():
yield from 'ABC'
yield from '123'
于是在merge执行循环时,A,B,C,1,2,3会依次返回。那么肯定会有人纳闷,为何不直接itertools.chain
呢?因为yield from有更复杂的功能,即能够让外部的generator对内部做消息传递。yield from 就像一个套子,无缝地将生成器merge了起来。
更详细的信息,可参考
它有什么用呢?asyncio.
如果你喜欢C#的await和async关键字,则一定会对它的表现印象深刻。异步函数再也不用像js那样做疯狂的回调,顿时代码清晰了很多。这些也是编译器大人的功劳。
详细的可参考:
看得其实还是晕乎乎的,尽管我自认为对yield和协程有了相对充分的了解却依然如此。
结语
大神说,不要重复造轮子。你会发现之前想做的所有事情,都有远比你想的优雅的多的实现。
不过问题在于,用了协程,yield from, asyncio,别人就看不懂我写的代码了,并且不得不升级到py3,可维护性顿时下降很多。因为我发现,就我身边会python的人来说,熟悉且能灵活使用以上那些语义的人不到百分之一。
有任何问题,随时交流。
相关文章
- 【python教程入门学习】PyCharm下载和安装教程(包含配置Python解释器)
- Python怎么输入小数和整数_python输入非负整数
- python 生成数组_Python创建数组「建议收藏」
- python整除和取余写法_Python的整除和取余[通俗易懂]
- Python入门系列(十)一篇学会python文件处理
- python hashlib_Python hashlib模块实例使用详解
- python3 gil锁_python gil 多线程
- Python编程 字节
- Python 冒泡排序_python
- python django 数据库_Apache+Mysql+PHP/Python简单项目
- python hexdump_笨办法学 Python · 续 练习 26:`hexdump`
- Python 生成随机数_python建立随机数列表
- Python异步: 定义、创建和运行协程(5)
- 【代码】Python多线程执行并且按原本顺序返回[详细注释]
- Python获取“双十一”商品评论,做词云分析,一个简单的案例教学
- Python基础(二十):面向对象基础
- Python迭代器是啥?
- python多进程通信、共享变量
- python-Python与PostgreSQL数据库-使用Python执行PostgreSQL查询(一)
- 用Python多线程抓取并验证代理详解编程语言
- python多线程模块threading使用范例代码详解编程语言
- 用Python简化Linux指令:从繁琐变简单(python运行linux命令)
- Python简易操作MySQL入门教程(python操作mysql)
- python使用Python轻松操作Redis(redis-)
- 用一个开源工具实现多线程 Python 程序的可视化
- 如何在Linux中安装Python?(linux安装python)
- Python在MSSQL中的应用实践(python与mssql)
- win7安装python生成随机数代码分享
- Python中多线程thread与threading的实现方法