上一篇介绍了atomic
包以及互斥锁 mutex
来解决并发竞争状态的问题。这一篇主要来介绍go
中与goroutine
常常搭档的好兄弟channel
缓存
channel
不只能够能够来用消除竞争状态,还能够用于不一样的goroutine
中进行通讯,发送与接受数据。chaanel的定义有两种,分为 有缓存与 无缓冲
chan1 := make(chan int) // 建立一个无缓冲的 整形 channel chan2 := make(chan int,2)// 建立一个有缓冲的 整形 channel
上面的代码片断,咱们分别建立了一个无缓冲的channel
与一个有缓冲的channel
。channel
的建立是使用make(chan type,[lenght])
来建立,若是指定了第二个参数length
表示建立了一个长度为length
的有缓存channel
,反之咱们称之为无缓冲。并发
var number int func main() { chan1 := make(chan int) //建立一个无缓冲的 整形channel go numberAdd(chan1) fmt.Printf("改变以后的number:%d\r\n",<-chan1) //改变以后的number:1 } func numberAdd(c chan int) { number++; c<-number; //往chan写值 }
这里咱们建立了一个整形的channel
在goroutine
中往chan
中写值,在main函数中取值。函数
goroutine
同时准备好才能完成发送和接收操做。若是两个 goroutine
没有同时准备好,通道会致使先执行发送或接收操做的 goroutine
阻塞等待。groutine
同时准备好。只有在通道中没有空间容纳新值的时候,发送动做才会发送阻塞;只有在通道中没有值要接收时,接收动做才会阻塞。下面来看两个例子atom
var wait sync.WaitGroup const needProcessNumber = 3 //须要三次加工 func main() { wait.Add(1) sausage := make(chan int) // 腊肠 go processing(sausage) //开始加工程序 sausage<-1 //开始第一次加工 wait.Wait() } func processing(sausage chan int) { defer wait.Done() for { nowNumber := <-sausage fmt.Printf("第%d次加工开始\r\n",nowNumber) for i:=1; i<=10; i++ { fmt.Printf("%d \r\n",i*10) } fmt.Printf("第%d次加工结束\r\n",nowNumber) if nowNumber==needProcessNumber{ fmt.Printf("新鲜的腊肠出炉了\r\n") close(sausage) return }else { go processing(sausage) //等待下一次加工开始 } nowNumber++ sausage <- nowNumber //这里会加锁直到流程交接结束 } }
这个例子建立了一个Int
无缓冲通道来表示腊肠,作一个腊肠须要三次加工,main
函数中建立了一个wait
来等待加工完成。准备一个加工的goroutine processing
等待第一个杯子准备就绪的信号,当接收到第一个信号时,开始加工,而后等待当前加工完成,若是当前goroutine
不是第三次加工的goroutine
,那么准备下一个加工程序开始,进入下一个goroutine
,直到第三次加工完成。spa
var wait sync.WaitGroup const ( maxTask = 10 //最大处理工做数 workerNumber = 3 //当前工人数 ) func main() { wait.Add(workerNumber) //等到全部的work都结束 tasks := make(chan int,maxTask) for workerOnline:=1;workerOnline<=workerNumber;workerOnline++ { go worker(tasks,workerOnline) } //增长十个须要处理的工做 for i:=1;i<=maxTask ; i++ { tasks<-i } close(tasks)//全部工做完成 wait.Wait() } //员工开始工做 func worker(task chan int,workNumber int) { defer wait.Done() for{ taskNumber,ok := <-task if !ok { fmt.Printf("工人:%d 没有工做能够作了\r\n",workNumber) return } fmt.Printf("工人:%d 开始工做,当前任务编号:%d\r\n",workNumber,taskNumber) workTime := rand.Int63n(100) time.Sleep(time.Duration(workTime)*time.Millisecond) fmt.Printf("工人:%d 工做完成,当前任务编号:%d\r\n",workNumber,taskNumber) } }
这里咱们声明了一个容量为10
的有缓冲通道task
来表示总共有十个任务须要3
个员工来处理。每一个员工是一个goroutine
来单独完成工做。员工首先准备就绪,而后等待任务的下发。当监听到有任务进入时,开始完成工做,直到监听到task
通道已经关闭。须要注意的是咱们在新增完10个任务时就已经关闭了channel
,这个时候goroutine
仍然能够从channel
取值,直到取到的返回数值是零值
,若是你这个时候获取了channel
的标志位,那么会返回一个false
,因此咱们判断channel
是否关闭应该用这个标志位来判断。code