上一篇介紹了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