Go并发编程入门怎么学_Go并发学习路线整理

12次阅读

go 并发核心在于理解 channel 阻塞语义、select 非抢占调度及 sync.Mutex 适用场景;需用 sync.WaitGroup 等同步机制避免主 goroutine 提前退出,防止循环变量复用导致数据错误,禁用 time.Sleep 做同步,避免 channel 读写不配对引发死锁,合理选择缓冲/无缓冲 channel,关闭 channel 前确保写端完成,select 随机选就绪分支且仅支持纯通信操作。

Go并发编程入门怎么学_Go并发学习路线整理

Go 并发不是靠“多开 goroutine”就能写对的,核心在于理解 channel 的阻塞语义、select 的非抢占式调度,以及何时该用 sync.Mutex 而非靠 channel 串行化。

goroutine 启动后就“消失”了?必须加同步机制

新手常以为 go f() 启动后函数会自然执行完,但主 goroutine 退出时整个程序立即终止,其他 goroutine 来不及执行。

  • sync.WaitGroup 显式等待:调用 wg.Add(1) 在启动前,wg.Done() 在 goroutine 结束时,主协程调用 wg.Wait()
  • 避免在循环中直接启动 goroutine 并复用循环变量(如 for _, v := range items { go func() { println(v) }() }),会导致所有 goroutine 看到同一个 v 的最终值;应传参:go func(val String) { println(val) }(v)
  • time.Sleep 不是同步手段,仅用于调试;生产代码中它掩盖了竞态,且无法保证等待足够久

channel 读写不配对就会死锁

向无缓冲 channel 发送数据会阻塞,直到有 goroutine 准备接收;若发送方和接收方没对齐(比如只发不收、或只收不发),fatal Error: all goroutines are asleep - deadlock! 立刻出现。

  • 无缓冲 channel(make(chan int))适合严格的一对一同步;有缓冲 channel(make(chan int, 10))可缓解生产者/消费者速率差异,但缓冲区满后仍会阻塞发送
  • 从已关闭的 channel 读取会立即返回零值;向已关闭的 channel 写入 panic,所以关闭前确保所有写端都已完成
  • select 配合 default 可实现非阻塞尝试读/写,避免卡住

select 是并发控制枢纽,不是 switch 的并发版

select 会在多个 channel 操作中**随机选择一个就绪的分支**执行,没有优先级,也没有“条件判断”逻辑——它只看 channel 是否可读/可写。

  • 所有 case 中的 channel 操作必须是纯通信动作(如 ch 或 ),不能带函数调用或赋值表达式
  • 如果多个 case 同时就绪,Go 运行时随机选一个,不可预测;不要依赖顺序
  • 想实现超时,用 time.After(d) 构造的 channel;想避免阻塞,加 default: 分支
  • select{} 会永久阻塞,等价于 for {},常用于让主 goroutine 等待信号
select { case msg := <-ch:     fmt.Println("received", msg) case <-time.After(2 * time.Second):     fmt.Println("timeout") default:     fmt.Println("no message ready") }

sync.Mutex 和 channel 不是二选一,而是分工明确

channel 用于 goroutine 间**传递数据与控制流**;sync.Mutex 用于保护**共享内存的临界区**。混用或误用会导致隐蔽 bug 或性能瓶颈。

  • 当多个 goroutine 需要读写同一结构体字段(如计数器、缓存 map)、且操作不是原子的,必须用 mu.Lock()/Unlock() 包裹,不能指望 channel 转发来“串行化”——那只是把竞争转移到了 channel 上
  • sync.RWMutex 适合读多写少场景,允许多个 reader 并发,但 writer 仍独占
  • 避免在持有 mutex 时调用可能阻塞或长时间运行的函数(如网络请求、channel 操作),否则会拖慢所有等待该锁的 goroutine

真正难的不是写出能跑的并发代码,而是判断某个状态是否被多个 goroutine 共享、哪些操作必须原子、channel 边界是否清晰——这些没法靠抄例子解决,得在 debug 竞态(go run -race)和重读 runtime.gopark 行为中慢慢建立直觉。

text=ZqhQzanResources