本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。
本文是 Python 进阶系列专题的最后一篇文章了,学习一下 Python 并发编程。懂得并发编程,就算是真正进阶了。
Python 多线程
Python 有真正的多线程吗?我本来以为是没有的。因为 CPython 解释器本身就不是线程安全的,因此有全局解释器锁(GIL),一次只允许使用一个线程执行 Python 字节码。换句话说,Python 即便是有多线程,也会受 GIL 限制,按顺序执行。所以我就以为 Python 是没有多线程的,也就是一个 Python 进程不能同时使用多个 CPU 核心。然而,Python 标准库中所有执行阻塞型 I/O 操作的函数,在等待操作系统返回结果时,都会释放 GIL,允许其他线程运行。这就意味着 Python 线程在 I/O 密集型应用中还是可以发挥作用的,比如一个 Python 线程等待网络响应时,阻塞型 I/O 函数会释放 GIL,再运行一个线程。再比如 time.sleep()函数也会释放 GIL。
Python 多进程
但是对于 CPU 密集型应用来说,要想利用所有可用的 CPU 核心,就得使用多进程,规避 GIL。
多线程与多进程对比
什么时候用多线程?什么时候用多进程?
I/O 密集型应用使用多线程,CPU 密集型应用使用多进程。
什么是 I/O 密集型应用?什么是 CPU 密集型应用?
简单来说,I/O 密集一般涉及到网络、磁盘 IO。而 CPU 密集指的是计算比较多。
创建多线程可以使用 concurrent.futures,创建多进程可以使用 multiprocessing。
多线程与协程对比
多线程存在着切换开销,同时为了避免写变量冲突,在控制共享资源时需要加锁,因此编写程序会比较复杂比较困难。而协程是通过单线程实现的并发,既能自由切换,也不需要锁机制,执行效率要高很多。
多线程和协程有个共同点是只对 I/O 密集型应用有效,因为 GIL 限制。如果想处理 CPU 密集型应用,那么可以结合多进程一起使用,以提高 CPU 使用率。
asyncio 包
asyncio 包比较多用来实现 Python 协程并发,原书在这一章节引用了很多示例,穿插了很多代码,导致我看起来有点乱,不是很清楚到底该怎么使用这个包。所以我看了一下官方文档:
https://docs.python.org/3/library/asyncio.html

从 Python3.5 开始引入了async 和 await,替代了@asyncio.coroutine 和 yield from 语法,语义更简洁更明确了。并且从 Python3.7 开始引入了asyncio.run(),替代了这一串代码:
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
创建 task 的语法也发生了变化,可以用asyncio.create_task:
async def coro():
...
## In Python 3.7+
task = asyncio.create_task(coro())
...
## This works in all Python versions but is less readable
task = asyncio.ensure_future(coro())
...
对于多个并行 task 可以用asyncio.gather,替代 asyncio.wait:
task_list = []
for i in range(5):
task = asyncio.create_task(async_func(i))
task_list.append(task)
done, pending = await asyncio.wait(task_list, timeout=None)
for done_task in done:
print((f"[{current_time()}] 得到执行结果 {done_task.result()}"))
task_list = []
for i in range(5):
task = asyncio.create_task(func(i))
task_list.append(task)
results = await asyncio.gather(*task_list)
for result in results:
print((f"[{current_time()}] 得到执行结果 {result}"))
所以对于第三方包的学习,最好是看看最新的官方文档,说不定已经进行了很多优化。
最后,完结,撒花。
我会把《流畅的 Python》这本书送给大家,感谢一直以来的关注。
参考资料:
《流畅的 Python》 第 17 章使用 future 处理并发 第 18 章使用 asyncio 包处理并发
https://blog.csdn.net/youanyyou/article/details/78990156
https://zhuanlan.zhihu.com/p/104918655