引言 本文是并发编程的系列的最后一篇, 将通过Demo
的方式, 尽可能的展示之前提到的技巧, 顺便做一个总结吧; 实现一个Demo
: 双协程交替打印.
Demo 为何做这个Demo
呢? 那是因为之前面试面到了, 但是很尴尬, 没写出来.
要求 给定一组数组, 要求使用两个协程交替打印数组;
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package mainimport ( "context" "fmt" "sync" ) func main () { nums := []int {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } in := make (chan int ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Add(2 ) go func () { defer wg.Done() for _, n := range nums { select { case <-ctx.Done(): return case in <- n: } } close (in) }() output := func (name string , control <-chan struct {}, nextControl chan <- struct {}) { defer wg.Done() for { select { case <-ctx.Done(): return case <-control: n, ok := <-in if !ok { return } fmt.Printf("%s Goroutine get number: %v\n" , name, n) nextControl <- struct {}{} } } } controlOne := make (chan struct {}) controlTwo := make (chan struct {}) go output("One" , controlOne, controlTwo) go output("Two" , controlTwo, controlOne) controlOne <- struct {}{} wg.Wait() }
效果 1 2 3 4 5 6 7 8 9 10 11 [root@VM-0-9-centos learnGo]# go run ./cmd/main.go One Goroutine get number: 1 Two Goroutine get number: 2 One Goroutine get number: 3 Two Goroutine get number: 4 One Goroutine get number: 5 Two Goroutine get number: 6 One Goroutine get number: 7 Two Goroutine get number: 8 One Goroutine get number: 9 Two Goroutine get number: 10
思路 整体思路: 实现生产者与消费者模型, 有一个生产者, 负责将数组内的元素发送到消费通道内, 供消费者处理; 由于题目要求两个协程交替打印, 那么需要存在两个消费者依次消费;
如何实现两个消费者依次 消费: 两个消费者均采用同步通道控制, A
消费完则告知B
可以消费, B
消费完则告知A
可以消费;
如何保证消费协程优雅退出: context
如何保证主程序与子协程之间的同步: sync.WaitGroup
流程:
初始化变量和通道 :在main
函数中,初始化整数切片nums
,创建一个整型通道in
用于存放数字,以及使用context.Background()
创建一个context.Context
对象ctx
和一个sync.WaitGroup
对象wg
。
**启动生产者goroutine
**:启动一个匿名的goroutine
,通过for range
循环遍历nums
切片,并向in
通道发送数字。在发送完毕后,关闭in
通道。select
语句中判断ctx.Done()
,若收到退出信号则退出循环。
**启动消费者goroutine
**:定义一个输出函数output
,用于消费数字并输出。通过goroutine
启动两个消费者,分别为”One”和”Two”。每个消费者通过select
语句监听ctx.Done()
和控制通道,接收到信号后输出数字并向下一个消费者发送信号。若通道关闭,则退出循环。
启动消费者流程 :向controlOne
通道发送一个信号,启动第一个消费者流程。等待所有goroutine
执行完毕,使用wg.Wait()
来等待。
通过使用context.Context
实现优雅退出和sync.WaitGroup
等待所有goroutine
执行完毕