PyPyBridge

从 goroutine 到 asyncio / 线程

goroutine+channel ↔ asyncio+Queue。GIL 让线程不是你以为的线程,CPU 密集请用 multiprocessing。

15 分钟 · 右栏代码可点击「运行」


Go 的并发是运行时调度的 M:N 协程,任何函数前加 go 就并发。Python 把并发拆成了三把刀:asyncio(IO 密集,单线程事件循环,最像 Go 的用法)、threading(IO 密集但依赖 C 扩展释放 GIL 的场景)、multiprocessing(CPU 密集,真并行)。选刀的原则:IO 等 → asyncio,算 → multiprocessing。

go func + WaitGroup → asyncio.gather

Go
1var wg sync.WaitGroup
2results := make([]string, 3)
3
4for i, name := range names {
5 wg.Add(1)
6 go func(i int, n string) {
7 defer wg.Done()
8 results[i] = work(n)
9 }(i, name)
10}
11wg.Wait()
Python
1import asyncio
2
3async def main():
4 results = await asyncio.gather(
5 *(work(n) for n in names)
6 )
7 return results
8
9asyncio.run(main())
  • WaitGroup / errgroup 的日常角色由 gather 承担;失败即取消的行为可以用 return_exceptions=True 关闭。

channel → Queue

Go
1ch := make(chan string, 8)
2
3go producer(ch)
4for msg := range ch {
5 fmt.Println(msg)
6}
Python
1import asyncio
2
3queue: asyncio.Queue[str] = asyncio.Queue(maxsize=8)
4
5async def consumer():
6 while True:
7 msg = await queue.get()
8 print(msg)
9 queue.task_done()
  • 线程版本用 queue.Queue,跨进程用 multiprocessing.Queue。没有 select 语句,用 asyncio.wait 组合实现。

注意

GIL:读这一段就够了Python 的线程被 GIL 串行化,threading 加速不了纯 CPU 计算(Go 没有这个概念)。IO 等待会释放 GIL 所以 IO 并发有效;真并行用 multiprocessing 或让 NumPy/C 扩展去算。Python 3.13 起实验性 free-threading 构建正在取消 GIL,但生产上还没到切换的时候。

练一练

这张卡的示例代码已在游乐场中备好,改一改再运行。

在游乐场中打开