题目
使用无缓冲通道实现两个goroutine交替打印数字和字母
信息
- 类型:问答
- 难度:⭐
考点
goroutine创建,channel同步,无缓冲通道
快速回答
通过两个无缓冲通道实现goroutine同步:
- 创建两个无缓冲通道
numChan和letterChan - 启动数字打印goroutine:发送数字→接收字母通道信号
- 启动字母打印goroutine:接收数字通道信号→发送字母
- 主goroutine初始化信号触发流程
原理说明
无缓冲通道的特性是发送和接收操作会阻塞,直到另一方准备好。利用这一特性:
- 通道A控制数字打印goroutine的执行
- 通道B控制字母打印goroutine的执行
- 两个goroutine通过互相发送信号实现严格交替
代码示例
package main
import "fmt"
func main() {
numChan := make(chan struct{}) // 控制数字打印
letterChan := make(chan struct{}) // 控制字母打印
// 数字打印goroutine
go func() {
for i := 1; i <= 26; i++ {
<-numChan // 等待信号
fmt.Print(i, " ")
letterChan <- struct{}{} // 通知字母goroutine
}
}()
// 字母打印goroutine
go func() {
for c := 'A'; c <= 'Z'; c++ {
<-letterChan // 等待信号
fmt.Print(string(c), " ")
numChan <- struct{}{} // 通知数字goroutine
}
}()
// 初始化:触发数字goroutine开始
numChan <- struct{}{}
// 防止主goroutine退出
var input string
fmt.Scanln(&input)
}
最佳实践
- 使用
struct{}{}作为信号,节省内存(空结构体大小为0) - 明确通道的发送/接收顺序避免死锁
- 主goroutine通过
fmt.Scanln保持运行(生产环境用sync.WaitGroup)
常见错误
- 死锁:通道操作顺序错误导致相互等待
- 遗漏初始化信号:未发送第一个触发信号
- 通道关闭不当:本例不需要关闭通道
- 未处理主goroutine退出:导致子goroutine未执行完
扩展知识
- 无缓冲通道本质:同步操作,发送和接收需在不同goroutine配对出现
- 带缓冲通道:允许暂存数据,但本例需严格交替,不适合使用
- 其他同步方式:sync.Mutex(复杂场景)、sync.WaitGroup(等待结束)
- 实际应用场景:生产者-消费者模型、流水线处理、任务调度