今天商汤面试 golang 岗位, 面试官没有按照常规出算法题, 而是出了道 go routine 交替打印的题目。由于没有准备相关的代码题, 大概率凉凉。特此记录一下。
事后复盘, 这道题还是相当坑的, 要是没有准备, 或者是 golang 并发编程不是特别精通, 短时间是想不到的。 很多人一开始想到的是 开两个 go routine , go routine 里面不停地读 一个 channel, 根据 channel 里面读出来的内容, 来决定在哪个 go routine 里面打印, go routine 打印完以后继续向 channel 里面写, 另一个 go routine 接着读。 比如 协程A读出来是0 ,则协程A 打印, 接着协程A往 channel写入1, 协程B 读出来是1 则 第协程B 打印, 接着往channel 写入0。这样做看起来是不错的, 但问题在于 : 从读出来以后, 如何确保 两个 go routine 交替往 go routine 写, 交替从 go routine 读
看到这你可能蒙蔽了,对啊,如果用 channel 去向两个 协程传递信号, 如何确保交替性呢。
事实上当你想到用 1个channel 去 通知两个 协程的时候读的时候就已经掉进坑里了。
正确的做法用两个 channel。协程A 从 chan 1 里面读, 协程A读完以后 往 chan 2 里面写, 协程B 再从 chan 2 里面读,读完再往 chan1 里面写。这里 chan1 一定要用带缓冲的 channel, 因为 chan1 被读以后不会立马有 协程来写,而是要等待协程 A 打印以后 写入 chan2, 协程B 读完 chan2 以后打印, 才会去写 chan2
package main
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{};
c1 := make(chan int,1);
c2 := make(chan int);
wg.Add(2);
go func() {
defer wg.Done()
for i:=0; i<10 ; i++ {
<- c1
fmt.Println("A")
c2<-1
}
}()
go func() {
defer wg.Done()
for i:=0; i<10 ; i++ {
<-c2
fmt.Println("B")
c1<-1
}
}()
c1 <-1;
wg.Wait()
}
复制代码
聪明的同学可能会问了,要是让你轮流在3个协程里面打印输出, 你会怎么办呢? 答案很简单, 和方法2 页码一模一样,用三个 chan, 三个协程轮流读channel, 写 channel,形成一个环。 同样第一个 chan 要用带缓冲的。
package main
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{};
c1 := make(chan int,1);
c2 := make(chan int);
c3 := make(chan int);
wg.Add(3);
go func() {
defer wg.Done()
for i:=0; i<10 ; i++ {
<- c1
fmt.Println("A")
c2<-1
}
}()
go func() {
defer wg.Done()
for i:=0; i<10 ; i++ {
<-c2
fmt.Println("B")
c3<-1
}
}()
go func() {
defer wg.Done()
for i:=0; i<10 ; i++ {
<-c3
fmt.Println("C")
c1<-1
}
}()
c1 <-1;
wg.Wait()
}
复制代码