【go】goroutine 交替打印又忘了?你该怎么不忘?

2023-12-25 23:01:40

说明:别再忘了,不会忘了,必须的

就是这题用来理解 goroutine 很不错,同时是大厂面试喜欢问的,毕竟这题不难,但重在你是否真的理解了,理解了就好记忆一点
但是记忆除了理解还有很多方法,今天就让你既理解,又记忆长久

题目:两个 goroutine 交替打印 1-100

或者字母数字什么啦,都一样,重在交替而已,别的只是一点点条件不同

接下来我会说两种,一种有缓冲,一种没缓冲,看懂一种,另一种都非常好理解和记忆了

1、有缓冲

现在呢,咱们别看代码,先想一下怎么写

  • 首先呢,肯定要声明两个 chan,例如chanOne,chanTwo,用make 这很简单不说了
  • 接着呢,要打印,因为是有缓冲的,假设咱们设置的缓冲大小是 1(这可能有别的问题,死锁,刚开始不用想这么多),
  • chanOne 先放进去一个随便
开始第一个 go func,协程
  • 接着开始写go func,协程,写一个 for 循环,i从1开始,每次自增2,我们从 chanOne 取出来,如果可以取出来,那就是可以执行下一步啦,就是打印 i,接着往 chanTwo 随便塞入一个数
  • 诶嘿,当第二次执行for发现 chanOne 空了,取不出来了,就被阻塞
  • 知识点哈,chan为空,取的时候会阻塞
开始第二个 go func ,协程
  • 是的,步骤和上边一样哈哈,此时,chanTwo 不为空,就可以执行 fmt.Print啦,往 chanOne 发一个随便的数字,诶嘿,此时chanOne的for又可以执行啦,从此循环即可啦
问题
  • 这样结束,不知道你发现没有,最后是不会打印的
  • 果然想象很美好,现实很骨感啊~~(别想你的小姐姐啦)
  • 仔细回顾一下,问题出在了这里,goroutine的执行顺序是不确定的,并不是我把chanOne写在了前边就执行前边的
  • 知识点:chan为空,读的时候也会阻塞
  • 所以如果先执行了第二个goroutine,就 chanTwo 本来就为空,就直接阻塞了,也就不会不会全部打印完,死锁了,一个在等待接受一个在等待发送,这就是设置缓冲大小为 1,同时又不做同步的问题,死锁,但其实就算你不设置为 1 ,设置 10 ,还是一样的道理的,还是设置 1 好点
解决办法

实现同步,同步的方式

  • time.Sleep(1 * time.Second) 一行解决,但不推荐
  • 用 WaitGroup ,推荐
  • 其实也可以用 Mutex ,但没必要,加锁性能就不好了
func main() {
	supersedeChannel()
}
func supersedeChannel() {
	// 两个 goroutine 交替打印法,两个chan
	chanOne := make(chan int, 1)
	chanTwo := make(chan int, 1)
	wg := sync.WaitGroup{}
	wg.Add(2)
	chanOne <- 1
	go func() {
		defer wg.Done()
		for i := 1; i <= 100; i += 2 {
			<-chanOne
			fmt.Printf("%v,", i)
			chanTwo <- i
		}
	}()

	go func() {
		defer wg.Done()
		for i := 2; i <= 100; i += 2 {
			<-chanTwo
			fmt.Printf("%v,", i)
			chanOne <- i
		}
	}()
	fmt.Println("嗯嗯嗯嗯嗯嗯嗯嗯呢")
	//time.Sleep(1 * time.Second) // 为什么加了这一行就能打印出来,不加这一行就打印不出来
	wg.Wait()
	fmt.Println("哈哈哈哈哈哈哈哈")
}
2、无缓冲

有缓冲懂了,这个无缓冲,那还不手到擒来

func main() {
	supersedeChannel()
}

func supersedeChannel() {
	// 两个 goroutine 交替打印法,使用 channel 同步
	chanOne := make(chan bool)
	chanTwo := make(chan bool)
	done := make(chan bool)

	go func() {
		defer close(done)
		for i := 1; i <= 100; i += 2 {
			<-chanOne
			fmt.Printf("%v,", i)
			chanTwo <- true
		}
	}()

	go func() {
		//defer close(chanOne)
		defer close(chanTwo)
		for i := 2; i <= 100; i += 2 {
			<-chanTwo
			fmt.Printf("%v,", i)
			chanOne <- true
		}
	}()
	fmt.Println("enennenenennenenene")
	chanOne <- true // 启动交替打印的循环

	<-done

	fmt.Println("\n哈哈哈哈哈哈哈哈")
}

文章来源:https://blog.csdn.net/EGXXM/article/details/135209141
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。