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实现按次序交替非常有意思,它背后的设计模式生产者消费者模式,大家可以在项目中使用起来,如果你觉得这篇文章对你有帮助,欢迎关注点赞转发哦。

- END -

公粽号:堆栈future

使很多处于迷茫阶段的coder能从这里找到光明,堆栈创世,功在当代,利在千秋

图片

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值