如果你还在用
sync.Mutex,那你就错过了 Go 并发最性感的部分:channel。
在 Go 世界,锁只是基础款,channel 才是艺术。
🧩 一、为什么大家讨厌锁?
先来看一段“经典死锁”事故现场:
var mu sync.Mutex
func f() {
mu.Lock()
defer mu.Unlock()
// 某些操作...
}
问题不大,对吧?但加上 goroutine 和多函数嵌套调用后,就变成这样:
- 谁先加锁?
- 谁忘记解锁?
- 是不是加了两次?
- 会不会卡死整段服务?
用锁像谈恋爱:开始简单,后来复杂、误解、崩溃。
📦 二、Go 提供了更优雅的办法:channel
Channel 是 Go 原生的通信机制,核心理念:不要通过共享内存来通信,而要通过通信来共享内存。
它能做什么?
- 多个协程之间传值
- 控制并发流量(像限流阀)
- 实现任务队列、工作池
🧪 三、channel 实战演示
🌰 场景1:两个协程同步执行
func main() {
done := make(chan bool)
go func() {
fmt.Println("goroutine: 你好!")
done <- true
}()
<-done
fmt.Println("main: 完成")
}
channel 在这里就像一个“信号枪”:通知任务完成。
🔁 场景2:worker 池实现
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("worker %d 处理 job %d\n", id, j)
time.Sleep(time.Second)
results <- j * 2
}
}
func main() {
jobs := make(chan int, 5)
results := make(chan int, 5)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
for r := 1; r <= 5; r++ {
<-results
}
}
✅ 优势:
- 自动调度
- 没有竞态
- 易于水平扩展
⛏ 四、channel 与 Mutex 的对比
| 特性 | channel | sync.Mutex |
|---|---|---|
| 学习成本 | 略高 | 低 |
| 可视化程度 | 明确传值 | 隐式状态 |
| 死锁风险 | 低(但不是没有) | 高(易忘解锁) |
| 用途 | 通信 + 同步 | 只同步 |
结论:channel 更适合 并发编排、任务流转、协程之间的数据交换,不是万能,但更优雅。
🧠 五、select:并发中的“调度大师”
select {
case msg1 := <-ch1:
fmt.Println("接收 ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("接收 ch2:", msg2)
default:
fmt.Println("都没有数据")
}
select 可以非阻塞地监听多个 channel,非常适合 多路监听、超时控制、优雅退出。
🔥 六、限流器示例:控制并发节奏
limit := make(chan struct{}, 10) // 同时最多10个
for i := 0; i < 100; i++ {
limit <- struct{}{} // 占位
go func(i int) {
defer func() { <-limit }() // 释放
doSomething(i)
}(i)
}
这种方式比 “手动统计 goroutine 数量 + sleep” 优雅太多!
😰 七、channel 也不是没缺点
- 无缓存 channel 容易阻塞,导致死等
- 写 channel 时没有人读取会 panic
- select 嵌套太多时可读性差
- 滥用 channel 可能导致性能下降(不如锁快)
正确使用 channel 的前提:清晰的协程通信逻辑 + 控制 channel 生命周期
✅ 总结一下:
| 应用场景 | 建议用法 |
|---|---|
| 数据共享 + 高性能 | sync.Mutex / atomic |
| 协程通信、任务分发、信号传递 | channel |
| 多任务监听、退出控制 | select + channel |
| 高并发下限流 | buffered channel |
🎬 下集预告
下一篇,我们将带你进入 Go 的“看不见的战场”——逃逸分析与垃圾回收,看看你写的每一行代码,是不是偷偷把内存“泄露”了。