面试题
这是一个比较经典的golang面试题,考察的是对channel的使用,下面是题目要求:
- 启动3个协程处理
- 3个协程依此输出 cat dog fish cat dog fish…依此类推
- 每个协程各输出100次,一共300次。
实现思路
我们将3个协程分别称为g0,g1和g2,由于要交替依次输出,则g0执行时,g1和g2需要阻塞等待,同时g0执行完后,需要通知g1执行,g1执行完后,需要通知g2执行,依此类推。
我们知道,当从一个无缓冲区的channel接收消息,或者从一个有缓冲区,但是缓冲区为空的channel接收消息,都会阻塞等待。这样我们就可以给每个打印协程都传入两个channel,一个用来接收消息执行,一个用来通知下一个协程执行。
同时,我们可以使用一个sync.waitGroup来阻塞主协程等待子协程全部执行完。
实现代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package main
import (
"fmt"
"sync"
)
// 面试题:每个函数起一个goroutine,轮流打印 cat dog fish 各100次
// 3个goroutine, 打印顺序是 cat dog fish cat dog fish ... 依此类推
var wg sync.WaitGroup
func main() {
chCatOk := make(chan struct{}, 1)
chDogOk := make(chan struct{}, 1)
chFishOk := make(chan struct{}, 1)
wg.Add(3) // 有3个协程,所以加3
go printAnimal("cat", chCatOk, chDogOk)
go printAnimal("dog", chDogOk, chFishOk)
go printAnimal("fish", chFishOk, chCatOk)
// 先通知cat执行
chCatOk <- struct{}{}
wg.Wait()
fmt.Println("执行结束")
}
func printAnimal(word string, ch1 <-chan struct{}, ch2 chan<- struct{}) {
count := 0
// 退出前标记完成
defer wg.Done()
for _ = range ch1 {
fmt.Println(word)
count++
ch2 <- struct{}{} // 通知协程2你可以执行了
if count == 100 {
return
}
}
}
|
代码执行
执行效果如下:

题后思考
上面的代码中,下面3行能否改成初始化无缓冲区的channel?如果不能,为什么?
1
2
3
|
chCatOk := make(chan struct{}, 1)
chDogOk := make(chan struct{}, 1)
chFishOk := make(chan struct{}, 1)
|
(完)