题目:循环按序打印"dog"、“cat”、fish",分别使用三个 goroutine.

拿到题目,感觉简单又不简单的样子,先动手写一个最简单的打印:

func main() {
	times := 100
	go func() {
		for i := 0; i<times; i++ {
			println("dog")
			println("cat")
			println("fish")
		}
	}()
	time.Sleep(100 * 1000) // 这时间我能算出来么,那用 channel 来接收吧
}

改进一下这让人无法预估的等待时间:使用 channel,在 goroutine 执行完之后通知主程序,巩固依稀 gorutine 和 channel 的基础知识:

  • gorutine 是并发核心,main 函数也是一个 gorutine
  • go func() {}() 匿名函数要注意参数传入
  • channel 通过 make 创建,make(chan typ) 需要声明好类型
  • channel 通过 <-ch ch <- data 来接收和发送信息
  • channel make(ch, int, 1 表示该 ch 是一个有1个数据缓冲的 chan,即在没有接收数据的情况下,第二条数据发送之后才会阻塞
  • func (ch chan <- int) 声明 ch 是一个单向 channel
  • close后的 chan 依然可以接收缓冲通道的数据,但不可发送数据
  • select {case <- ch: xx} 处理不同消息
func main() {
	waitCh := make(chan struct{}, 1)
	times := 100
	go func() {
		for i := 0; i < times; i++ {
			println("dog")
			println("cat")
			println("fish")
		}
		waitCh <- struct{}{}
	}()
	<-waitCh
}

waitCh 听起来怎么这么熟悉呢?go 本身在面对这些情况已经实现了一个工具类,sync 包中提供的基础原语: waitGroup 。巩固一下:

  • 官方描述:一个 WaitGroup 对象可以等待一组协程结束
  • 我们可以通过 sync.WaitGroup 将原本顺序执行的代码在多个 Goroutine 中并发执行,加快程序处理的速度。
  • 该类暴露三个接口:add wait done
  • 废话别说,show me the code 🙂
func main() {
	wg := sync.WaitGroup{}
	times := 100
	wg.Add(1) // 协程数
	go func() {
		for i := 0; i < times; i++ {
			println("dog")
			println("cat")
			println("fish")
		}
		wg.Done() // 协程执行完毕,一般复杂业务会有容错,确保执行: defer wg.Done()
	}()
	wg.Wait()
}

实现三个直接打印的 goroutine 必然不能保证有序,那就必须在 goroutine 间通信,cha -> chb -> chc->cha 循环触发则可以实现该功能。

func main() {
	wg := sync.WaitGroup{}
	wg.Add(3)

	dogCh := make(chan struct{}, 1)
	catCh := make(chan struct{}, 1)
	fishCh := make(chan struct{}, 1)

	times := 10
	dogCounter, catCounter, fishCounter := 0, 0, 0
	go PrintTheVal(dogCh, catCh, dogCounter, times, &wg, "dog") // &wg 需要取地址
	go PrintTheVal(catCh, fishCh, catCounter, times, &wg, "cat")
	go PrintTheVal(fishCh, dogCh, fishCounter, times, &wg, "fish")

	dogCh <- struct{}{}
	wg.Wait()
}

func PrintTheVal(selfCh, toCh chan struct{}, counter, max int, wg *sync.WaitGroup, val string) {
	for {
		<- selfCh
		toCh <- struct{}{}
		if counter >= max {
			wg.Done()
			return
		}
		println(val)
		counter++
	}
}

总结

  1. 通过简单的题目能快速理解基础语法,也能在实现简单题目中发现不足和巩固基础。
  2. 发散思维:其他同步原语的使用?