Python并发编程

Python并发编程笔记.

多线程

进程、线程和协程的关系:

  • 多进程能够利用多核优势,但是进程间通信比较麻烦,另外,进程数目的增加会使性能下降,进程切换的成本较高。程序流程复杂度相对I/O多路复用要低。
  • I/O多路复用是在一个进程内部处理多个逻辑流程,不用进行进程切换,性能较高,另外流程间共享信息简单。但是无法利用多核优势,另外,程序流程被事件处理切割成一个个小块,程序比较复杂,难于理解。
  • 线程运行在一个进程内部,由操作系统调度,切换成本较低,另外,他们共享进程的虚拟地址空间,线程间共享信息简单。但是线程安全问题导致线程学习曲线陡峭,而且易出错。
  • 协程有编程语言提供,由程序员控制进行切换,所以没有线程安全问题,可以用来处理状态机,并发请求等。但是无法利用多核优势(通过协程+进程的方式来解决)。

concurrent.futures

仿Java实现,执行并行任务

  • ThreadPoolExecutor
  • ProcessPoolExecutor

GIL(Global Interpreter Lock)

Python的内存管理不是 线程安全的,所以GIL被创造出来避免多线程同时运行同一个Python代码

异步IO

  • asyncio 内置库
  • Trio
  • Curio另一种实现,偏底层

gevent

  • 非阻塞的网络协程库,基于系统底层的libev事件库. hub(可以理解为MainThread)对应event loop.
  • gevent.spawn(func, args):创建一个greenlet,并将该greenlet的switch()加入hub主循环回调
  • join会保存当前greenlet.switch到一个队列中,并注册_notify_links回调,然后切换到hub,在_notify_links回调中将依次调用先前注册在队列中的回调。
  • 事件(event)是一个在Greenlet之间异步通信的形式。事件对象的一个扩展是AsyncResult,它允许你在唤醒调用上附加一个值。 它有时也被称作是future或defered,因为它持有一个指向将来任意时间可设置 为任何值的引用。
  • 范围为1的信号量也称为锁(lock),它向单个greenlet提供了互斥访问。 信号量和锁常常用来保证资源只在程序上下文被单次使用。
  • 上下文切换即两个子任务之间的切换。
# 放在文件头,其他导入语句前
# 会替换线程为greenlets
from gevent.monkey import patch_all
patch_all()

from gevent import getcurrent
id(getcurrent())