从 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.WaitGroup2results := make([]string, 3)34for 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 asyncio23async def main():4 results = await asyncio.gather(5 *(work(n) for n in names)6 )7 return results89asyncio.run(main())
- WaitGroup / errgroup 的日常角色由
gather承担;失败即取消的行为可以用return_exceptions=True关闭。
channel → Queue
Go
1ch := make(chan string, 8)23go producer(ch)4for msg := range ch {5 fmt.Println(msg)6}
Python
1import asyncio23queue: asyncio.Queue[str] = asyncio.Queue(maxsize=8)45async 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,但生产上还没到切换的时候。
练一练
这张卡的示例代码已在游乐场中备好,改一改再运行。