题目:
现在有两个goroutine。
一个输出1、3、5、7、9……
另一个输出2、4、6、8、10……
写一段代码,让他们输出1、2、3、4、5、6、7、8、9、10……
解法:
使用Go的channel来解决比较合适。
需要3个channel。
A通道用来记录A协程的状态。
B通道用来记录B协程的状态。
Exit通道用来阻塞主协程,使程序不要立即退出,而是等待我们发出退出信号以后才退出。
Go的channel具有阻塞特性,无缓冲通道中只能存放一个数据。
通道最初是空的,如果想从空通道中读取一个数据,程序就会阻塞,直到向通道中写入一个数据。
如果通道中有一个数据,却没有人来读,程序也将阻塞,直到有人将这个数据读走。
利用这个特性,我们在主goroutine中读取Exit通道,因为我们还没有向Exit通道中写数据,主goroutine将在此阻塞。
主goroutine虽然阻塞了,其他goroutine却是可以正常运行的。
我们设计,其他goroutine把自己的工作都做完之后,向Exit通道写入一个数据。
主goroutine得到这个数据之后,程序不再阻塞,就可以正常退出了。
这样我们就实现了最基本的,有多个goroutine时,工作没做完时等待,都做完后退出的功能了。
然后我们来设计AB两个通道。
利用无缓冲通道阻塞的特性,我们设计:
A协程输出一个数后,向B通道写入一个数据。
B协程输出一个数后,也向A通道写入一个数据。
AB都会时刻检测自己的通道中有没有数据,但是就像接力棒一样,只有另一个协程可以向当前协程的通道传递数据。
而且传递完之后必须等待。
这就保证了,A干活的时候,B必须等着,不能跟A抢活干。
A干完之后,权利交给B,B干完之后,权利再交给A,直到俩人都把活干完。
这就是AB协程交替输出的中间过程,我们还需要设计开始和结束。
开始时,我们在主goroutine中,手动向A协程发送一个开始的信号。
结束时,最后一个把活干完的人(B),向Exit通道中发送一个数据,使主goroutine退出。
实现代码:
```go
package main
import (
"fmt"
)
func main() {
// 创建3个channel,A,B和Exit
A := make(chan bool)
B := make(chan bool)
Exit := make(chan bool)
go func() {
// 如果A通道是true,我就执行
for i := 1; i <= 10; i += 2 {
if ok := <-A; ok {
fmt.Println("A 输出", i)
B <- true
}
}
}()
go func() {
defer func() { Exit <- true }() // 这个协程的活干完之后,向主goroutine发送信号
// 如果B通道是true,我就执行
for i := 2; i <= 10; i += 2 {
if ok := <-B; ok {
fmt.Println("B 输出", i)
if i != 10 { // r如果i等于10了,就不要再向A通道写数据了,否则将导致A通道死锁,至于为什么,坦白说我很疑惑
A <- true
}
}
}
}()
A <- true // 启动条件
<-Exit // 结束条件
}
```
有疑问加站长微信联系(非本文作者))