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(通信顺序进程)是一种并发的模型,通过消息传递来进行交互,而不是通过共享变量。