Python并发方式

在 Python 中,早期并发方式以传统的多进程和多线程为主,类似 Java,同时,有不少第三方的异步方案(gevent/tornado/twisted 等)。
在 Python 3 时期,官方推出了 asyncio 和 async await 语法,作为 Python 官方的协程实现,而逐渐普及。

进程

多进程编程示例:

from multiprocessing import Process

def f(name):
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

multiprocessing 与 threading 的 API 接近,比较容易创建多进程的程序,是 Python 官方推荐作为绕过多线程 GIL 限制的一种方案。
但需要注意,创建进程的参数需要能被 pickle 序列化,最好使用 Pipe、Queue 等进程安全的数据结构(官方文档的 Programming guidelines

线程

多线程代码示例:

from threading import Thread

def f(name):
    print('hello', name)

if __name__ == '__main__':
    p = Thread(target=f, args=('bob',))
    p.start()
    p.join()
# 线程池方式
with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(pow, 323, 1235)
    print(future.result())

Cpython 线程的缺陷:GIL(全局解释器锁)
GIL 是 Cpython 执行 Python 字节码时的一把全局锁,导致解释器在 CPU 密集型任务时不能充分利用多核,而 IO 密集型任务会释放 GIL。
如果想绕过 GIL,只能换成多进程方式,或者通过C 扩展绕过。

协程

asyncio示例:

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")
    await asyncio.gather(say_after(1, 'hello'), say_after(2,'world'))
    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())
# started at 22:32:23
# hello
# world
# finished at 22:32:25

async 语法 与 asyncio

Python 从 3.4 版本开始,标准库自带 asyncio 模块,并从 3.5 开始,支持 async/await 语法。
Python 协程的实现可以追溯到 Python 2 时期引入的 yield 关键字和生成器这种特殊结构:

  • 生成器通过 yield 暂停,并且可以返回值
  • 调用方通过 next() 或者 send() 方法恢复生成器的运行,并且可以通过 send() 发送数据给生成器
  • yield from 语法糖可以方便的迭代生成器中每一个值
  • 通过引入 async/await 语法,正式确立协程类型
  • asyncio 库提供了官方的事件循环实现,并且支持不同操作系统的 io 多路复用(select/epoll/iocp 等),或者可以通过配置替换为第三方实现(如 uvloop)
  • 借助 concurrent.futures 线程池/进程池 模块,支持多线程/多进程,但事件循环本身依旧是单线程模式

Go 并发方式

goroutine 与 channel 示例:

package main

import "fmt"

func main() {
   messages := make(chan string)
   go func() { messages <- "ping" }()
   msg := <-messages
   fmt.Println(msg)
}

goroutine 与 channel

Golang 实现了用户态的协程 goroutine,通过GPM模型来进行协程的调度,
并且通过 netpoller 来支持网络的IO多路复用;
通过 channel 在不同 goroutine 中进行通信。

CSP

CSP(通信顺序进程)是一种并发的模型,通过消息传递来进行交互,而不是通过共享变量。

对比

有栈协程与无栈协程

单线程与多线程