10

Python 与 Go 之间的并发模式差异

 2 years ago
source link: https://ceresca.github.io/posts/concurrency-model-differences-between-python-go/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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模型来进行协程的调度, 1*2o9fS2rEJltFYom65wAIyw.jpeg
并且通过 netpoller 来支持网络的IO多路复用;
通过 channel 在不同 goroutine 中进行通信。

CSP

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

对比

有栈协程与无栈协程

单线程与多线程


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK