go學習筆記-goroutine的好兄弟channel

上一篇介紹了atomic包以及互斥鎖 mutex來解決併發競爭狀態的問題。這一篇主要來介紹go中與goroutine常常搭檔的好兄弟channel緩存

channel不只能夠能夠來用消除競爭狀態,還能夠用於不一樣的 goroutine中進行通訊,發送與接受數據。chaanel的定義有兩種,分爲 有緩存無緩衝
建立channel
chan1 := make(chan int) // 建立一個無緩衝的 整形 channel
chan2 := make(chan int,2)// 建立一個有緩衝的 整形 channel

上面的代碼片斷,咱們分別建立了一個無緩衝的channel與一個有緩衝的channelchannel的建立是使用make(chan type,[lenght])來建立,若是指定了第二個參數length表示建立了一個長度爲length的有緩存channel,反之咱們稱之爲無緩衝。併發

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寫值
}

這裏咱們建立了一個整形的channelgoroutine中往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

期待一塊兒交流

短腿子猿

相關文章
相關標籤/搜索