理解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í)行。