Go中按次序交替打印1212...,你知道它背后的设计模式吗

让你goroutine交替打印1212...【公粽号:堆栈future】
原文
首先这道题看似是两个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实现按次序交替非常有意思,它背后的设计模式生产者消费者模式,大家可以在项目中使用起来,如果你觉得这篇文章对你有帮助,欢迎关注点赞转发哦。

你可能感兴趣的:(Go中按次序交替打印1212...,你知道它背后的设计模式吗)