题目
使用无缓冲 Channel 实现两个 Goroutine 的同步
信息
- 类型:问答
- 难度:⭐
考点
无缓冲 Channel 特性, Goroutine 同步, Channel 阻塞机制
快速回答
通过无缓冲 Channel 的阻塞特性实现 Goroutine 同步:
- 无缓冲 Channel 发送和接收操作会相互阻塞
- 先启动的 Goroutine 在发送数据时会阻塞,直到另一个 Goroutine 准备好接收
- 通过这种机制确保两个 Goroutine 按顺序执行
原理说明
无缓冲 Channel 的特性是:发送操作会阻塞直到有 Goroutine 接收数据,接收操作会阻塞直到有 Goroutine 发送数据。利用这个特性可以实现 Goroutine 间的同步,确保执行顺序。
代码示例
package main
import "fmt"
func main() {
// 创建无缓冲 channel
ch := make(chan struct{})
// Goroutine 1
go func() {
fmt.Println("Goroutine 1 执行")
ch <- struct{}{} // 发送信号(阻塞直到被接收)
}()
// Goroutine 2
go func() {
<-ch // 接收信号(阻塞直到有数据)
fmt.Println("Goroutine 2 执行")
}()
// 防止主 Goroutine 提前退出
var input string
fmt.Scanln(&input)
}
执行结果
Goroutine 1 执行
Goroutine 2 执行关键机制
- 阻塞同步:Goroutine1 在
ch <- struct{}{}处阻塞,直到 Goroutine2 执行<-ch - 执行顺序保证:Goroutine2 必须等到接收数据后才能继续执行
最佳实践
- 使用
struct{}{}作为信号量(零内存开销) - 明确 channel 的发送/接收方职责
- 对于复杂同步场景可结合
sync.WaitGroup
常见错误
- 忘记接收导致死锁(所有 Goroutine 阻塞)
- 在主 Goroutine 中误用无缓冲 channel 导致阻塞
- 未处理可能的 channel 泄漏
扩展知识
- 有缓冲 Channel:当缓冲区未满时发送不阻塞,可实现异步通信
- Channel 关闭原则:由发送方关闭 channel,接收方通过
v, ok := <-ch判断状态 - Select 语句:用于同时监控多个 channel 的状态