题目
编写一个程序,实现以下功能:
-
创建一个协程,用于打印数字1到10;
-
创建另一个协程,用于打印字母A到J;
-
确保数字和字母的输出交替进行;
-
主线程等待两个协程完成后退出。
要求:
-
使用Go语言的协程(goroutine)实现并发;
-
使用适当的同步机制,以确保数字和字母的输出交替进行;
-
代码结构清晰,包含适当的注释。
先看代码
func main() {
var wg sync.WaitGroup
// 需要等待2个协程完成
wg.Add(2)
// 使用channel来控制输出顺序
numCh := make(chan bool, 1)
letterCh := make(chan bool, 1)
// 启动数字输出协程
go func() {
// 通知waitGroup 一个协程已经完成
defer wg.Done()
defer close(letterCh)
for i := 1; i <= 10; i++ {
<-numCh
fmt.Printf("%d ", i)
letterCh <- true
}
}()
// 启动字母输出协程
go func() {
// 通知waitGroup 一个协程已经完成
defer wg.Done()
defer close(numCh)
for c := 'A'; c <= 'J'; c++ {
<-letterCh
fmt.Printf("%c ", c)
numCh <- true
}
}()
// 启动输出
numCh <- true
// 等待两个协程完成
wg.Wait()
}
输出结果:
1 A 2 B 3 C 4 D 5 E 6 F 7 G 8 H 9 I 10 J
思路
实现交替
本题的关键在于数字和字母的输出要交替进行,那么如何实现交替呢?我们都知道,两个协程同时开启时,执行的先后顺序是不确定的。而 channel 就可以很好的解决这个问题。
我们把channel理解成是高速路口的起落杆,遵循一车一杆
的原则,杆子抬起表示从channel中取出数据,杆子落下表示无法从channel中取出数据(阻塞)。
杆子抬起,输出数字,杆子落下。杆子抬起,输出字母,杆子落下。杆子抬起,输出数字,杆子落下。。。如此往复。
实现主线程等待协程完成
为避免协程还未结束,主线程就已经结束了的情况,我们这里用到了sync.WaitGroup
来解决这个问题。
WaitGroup 对象内部有一个计数器,最初从0开始,它有三个方法:Add(),Done(),Wait()用来控制计数器的数量。Add(n)把计数器的值设置为n,Done() 每次把计数器的值 -1,wait()会阻塞代码的运行,直到计数器地值减为0。
关闭channel
为避免出现内存泄漏等问题,我们在使用完channel时,需要通过函数close()
将其关闭。
但是在本题中,channel的发送数量和接收数量都确定且相等,所以不会出现内存泄漏问题,故不关闭也行。