让你goroutine交替打印1212...
首先这道题看似是两个goroutine交替打印,实则是有很多细节需要考虑,而且背后的设计模式就是生产者消费者模式。
代码演示下简单的生产者消费者模式:
package main import "fmt" func main() { done := make(chan bool) //控制结束退出的信号 ch := make(chan int) //chan通信 控制同步阻塞的 go func() { for i:=1; i<=2; i++ { //生产 ch <- 0 fmt.Println("1") } //发送结束之后退出 done <- true }() go func() { for i:=1; i<=2; i++ { //消费 <- ch fmt.Println("2") } }() <-done } 输出: 1 1 2 2
但是题目是让两个goroutine交替输出1212...而且还必须保证次序,比如A goroutine是永远打印1,B goroutine永远打印2 这样循环2次,输出结果应该是1212。
那么为什么上面的代码输出是1122呢?因为我们控制了开始的次序,但是并没有控制结束的次序,发生了并发不安全的情况。
当然你不能说这种方式就不适合别的场景,比如交替打印奇偶数,就增加一个条件判断就行:
package main import "fmt" func main() { done := make(chan bool) //控制结束退出的信号 ch := make(chan int) //chan通信 控制同步阻塞的 go func() { for i:=1; i<=5; i++ { //生产 ch <- 0 if i % 2 == 0 { fmt.Println("A ", i) } } //发送结束之后退出 done <- true }() go func() { for i:=1; i<=5; i++ { //消费 <- ch if i % 2 != 0 { fmt.Println("B ", i) } } }() <-done } 输出: B 1 A 2 B 3 A 4
B永远输出奇数,A永远输出偶数,它们交替打印。
好了言归正传,那我们如何写出并发安全的生产者消费者代码呢?我们用首尾两个chan交替释放控制权就可以解决这个问题,请看代码:
package main import "fmt" func main() { done := make(chan bool) //控制结束退出的信号 chStart := make(chan int) //chan通信 控制起始同步阻塞的 chEnd := make(chan int) //chan通信 控制结尾同步阻塞的 go func() { for i:=1; i<=2; i++ { //生产 控制起始 chStart <- 0 fmt.Println("A ", 1) //暂停 等待结尾信号 <-chEnd } //发送结束之后退出 done <- true }() go func() { for i:=1; i<=2; i++ { //消费 结束起始 <- chStart fmt.Println("B ", 2) //发送结尾信号 chEnd <- 0 } }() //阻塞等待退出信号 <-done } 输出: A 1 B 2 A 1 B 2
现在可以看出来,输出的结果是正确的。为什么这么操作呢?就是当生产者生产之后把自己阻塞,等待消费者消费,消费者消费完成之后发送信号唤醒生产者继续生产,而消费者自己又被阻塞等生产者唤醒,其实这就是典型的生产者消费者模式。
小结
用两个chan实现按次序交替非常有意思,它背后的设计模式生产者消费者模式,大家可以在项目中使用起来,如果你觉得这篇文章对你有帮助,欢迎关注点赞转发哦。