2023-05-27 Go協(xié)程goroutine與通道channel

理解go與channel的關(guān)系

sync.WaitGroup

使用sync.WaitGroup等待一組并發(fā)操作完成。

var wg sync.WaitGrop
func hello(i int){
    defer wg.Done()
    fmt.Println("hello",i)
}
func main(){
    for i:=0;i<10;i++{
        wg.Add(1)
        go hello(i)
    }
    wg.Wait()  //在這里阻塞等待go結(jié)束
    fmt.Println("hello world")
}

channel 管道

通道的作用是用于兩個協(xié)程之間的通信:

分類:

無緩沖channel;

有緩沖channel。

//整形無緩沖通道
unbuffered := make(chan int)
//整形有緩沖通道
buffered := make(chan int 10)

功能:

無緩存通常用于同步通信

有緩存通常用于異步通信

語法:

//整形無緩沖通道
unbuffered := make(chan int)
//整形有緩沖通道
buffered := make(chan int 10)

//無緩沖管道使用
ch1 := make(chan string)  //初始化一個無緩沖字符串管道
ch1 <- "hello world"        //發(fā)送數(shù)據(jù)到管道(在這里就阻塞了)
data :=<- ch1               //從管道接受數(shù)據(jù)
close(ch1)                  //關(guān)閉管道

//有緩沖管道使用
    //情況1:讀取堵塞
  ch1 := make(chan int,5)  //大小為5的管道
  ch1 <- 10         //發(fā)送數(shù)據(jù)到管道
  ch1 <- 20       //發(fā)送數(shù)據(jù)到管道
  data :=<- ch1                     //從管道接受數(shù)據(jù)
  data2 :=<-ch1               //從管道接受數(shù)據(jù)
  data3 :=<- ch1
  fmt.Printf("data: %v\n", data)  
  fmt.Printf("data2: %v\n", data2)
  fmt.Printf("data3: %v\n", data3)  //因為管道中只有2個數(shù)據(jù),讀取第三個時會阻塞
  close(ch1)
    //情況2:發(fā)送阻塞
  ch1 := make(chan int,1)  //大小為1的管道
  ch1 <- 10         //發(fā)送數(shù)據(jù)到管道
  ch1 <- 20       //發(fā)送第二個數(shù)據(jù)到管道時會阻塞,因為管道的大小只有1
  data :=<- ch1                     //從管道接受數(shù)據(jù)
  fmt.Printf("data: %v\n", data)
  close(ch1)
    //情況三:無阻塞
  ch1 := make(chan int,5)  //字符串管道
  ch1 <- 10         //發(fā)送數(shù)據(jù)到管道
  ch1 <- 20       //發(fā)送數(shù)據(jù)到管道
  data :=<- ch1                     //從管道接受數(shù)據(jù)
  data2 :=<-ch1               //從管道接受數(shù)據(jù)
  fmt.Printf("data: %v\n", data)
  fmt.Printf("data2: %v\n", data2)
  close(ch1)

goroutine與channel

for range從channel中取值

當(dāng)通道被關(guān)閉后,會在通道內(nèi)的所有值被接收完畢后會自動退出循環(huán)。ps:如果沒有關(guān)閉通道會死鎖。

package main
import (
    "fmt"
    "sync"
)
var wg sync.WaitGroup
func f1(ch chan int){
  defer wg.Done()
  for x:=range ch{  //不斷地從管道讀取數(shù)據(jù),直到管道被關(guān)閉,如果管道沒有被關(guān)閉,會無法退出造成死鎖
    fmt.Println(x)
  }
}
func main() {
  ch := make(chan int,10)
  wg.Add(1)
  go f1(ch)
  for i:=0;i<11;i++ {
    ch<-i
  }
  close(ch)    //這里不關(guān)閉管道會死鎖
  wg.Wait()
}

當(dāng)通道被關(guān)閉后,依舊可以從通道中讀取數(shù)據(jù),如果通道中已經(jīng)沒有數(shù)據(jù)那么會讀到0值。

func main() {
  ch := make(chan int,2)
  ch<-10
  ch<-20
  close(ch)         
  //關(guān)閉管道后依然可以讀到管道內(nèi)的數(shù)據(jù),for range只會讀去管道存在的數(shù)據(jù)
  for x:=range ch { 
    fmt.Printf("x: %v\n", x)
  }
  //關(guān)閉了管道依然可以讀,管道沒有數(shù)據(jù)了,就會讀到0值,ok==false
  x,ok:=<-ch
  fmt.Printf("x: %v\n", x)
  fmt.Printf("ok: %v\n", ok)
}

select

select可以從多個通道接收值,使用它可以同時響應(yīng)多個通道的操作。

每個 case 分支會對應(yīng)一個通道的通信(接收或發(fā)送)過程。select 會一直等待,直到其中的某個 case 的通信操作完成時,就會執(zhí)行該 case 分支對應(yīng)的語句。

select 具有以下特點:

  • 處理一個或多個 channel 的發(fā)送/接收操作。

  • 如果多個 case 同時滿足,select 會隨機(jī)選擇一個執(zhí)行。

  • 對于沒有 case 的 select 會一直阻塞,可用于阻塞 main 函數(shù),防止退出。

    ch := make(chan int, 1)
    for i := 1; i <= 10; i++ {
        select {  //select處理的是一個發(fā)送或者讀取操作,如下對同一個管道的兩個不同操作。
            case x := <-ch:
              fmt.Println(x)
            case ch <- i:
            }
    }
    

select用于處理異步IO操作總結(jié)

1.select 會監(jiān)聽case語句中的讀寫操作,當(dāng)case中的channel讀寫操作為非阻塞狀態(tài),將會觸發(fā)相應(yīng)的動作。

2.如果有多個case可以執(zhí)行,select會隨機(jī)公平地選出一個執(zhí)行,其他不會執(zhí)行。

3.如果沒有可執(zhí)行地case語句,且有default語句,就會執(zhí)行default語句。

4.如果沒有可運(yùn)行地case語句,且沒有default語句,select將阻塞,直到有case能執(zhí)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容