introduction
類似unix中的管道(pipe),或是隊列(queue)
channel為引用類型的數據結構
先進先出
線程安全,多個goroutine同時訪問,不需要加鎖
channel是有類型的,整數的channel只能存放整數,依此類推
使用make()
初始化管道
聲明 1 2 3 4 5 6 var 變量名 chan 類型var test chan int var test1 chan string var test2 chan map [string ]string var test3 chan stu var test4 chan *stu
通常變量名會希望知道其為管道,因此會將chan結合其他關鍵字作為變量名1 2 var IntChan chan int var StrChan chan string
操作管道 example 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package mainimport "fmt" func main () { var intChan chan int intChan = make (chan int ,10 ) mapChan := make (chan map [string ]string ,10 ) intChan <- 10 a := make (map [string ]string ,0 ) a["Name" ] = "Curtis" mapChan <- a close (intChan) close (mapChan) for i := range intChan { fmt.Println(i) } for i := range mapChan { fmt.Println(i) } }
result
example 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package mainimport "fmt" func main () { UserChan := make (chan user,10 ) UserPtrChan := make (chan *user,10 ) var User1 user = user{ UserName:"Tom" , UserAge:18 , } UserChan <- User1 UserPtrChan <- &User1 close (UserChan) close (UserPtrChan) for i := range (UserChan) { fmt.Println(i) } for i := range (UserPtrChan){ fmt.Println(*i) } }
若管道類型為interface{}
則可以接受任何類型數據的寫入
result
tips 從channel讀取數據 1 2 a = <- testChan b := <- testChan
從channel寫入數據
channel和goroutine相結合 使用goroutine搭配channel實現讀寫
example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport ( "time" "fmt" ) var dataChan chan int = make (chan int ,1 )func WriteData (i int ) { dataChan <- i } func ReadData () { data := <- dataChan fmt.Println(data) } func main () { for i:=0 ;i<10 ;i++{ go WriteData(i) } for i:=0 ;i<10 ;i++{ go ReadData() } time.Sleep(time.Second*5 ) }
result
阻塞
若是存放進去管道的數據數量超過管道的容量又沒有其他協程取出數據,管道會一直阻塞等待數據被取出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport ( "time" "fmt" ) var dataChan chan int = make (chan int ,5 )func WriteData (i int ) { dataChan <- i fmt.Println("input the data" ,i) } func ReadData () { data := <- dataChan fmt.Println("get the data:" ,data) } func main () { for i:=0 ;i<10 ;i++{ go WriteData(i) } for i:=0 ;i<10 ;i++{ time.Sleep(time.Second) go ReadData() } time.Sleep(time.Second*5 ) }
result 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 input the data 5 input the data 8 input the data 0 input the data 1 input the data 7 get the data: 1 input the data 9 get the data: 0 input the data 6 get the data: 5 input the data 3 get the data: 8 input the data 2 get the data: 7 input the data 4 get the data: 9 get the data: 6 get the data: 3 get the data: 2 get the data: 4
從上述可看到寫入的協程馬上寫入了5組數據,但要再寫入時因為管道已經滿了,必須等待讀 的協程取出數據,才能在寫入數據到管道中
檢測channel是否被關閉
使用close()
函數關閉channel
在關閉channel後,管道只可讀(取出)不可再寫入
在取數據時可判斷管道是否已關閉,將最後一個數據取出時便會停止再取出數據
安全的取出數據,才不會造成死循環
val,ok := <- channel
當ok
的值返回false
時代表已從關閉 的管道中取出最後一條數據
example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" func main () { var intChan chan int intChan = make (chan int , 10 ) for i:=0 ;i<10 ;i++{ intChan <- i } close (intChan) for { var b int b,ok := <- intChan if !ok { break } fmt.Println(b) } }
要是沒有使用ok
判斷已關閉的管道 是否還有下一條數據時,for
循環會不斷的訪問管道造成死循環
result
channel之間的同步
example 1
查找1-100的所有質數1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package mainimport "fmt" func calc (i int ,ResultNum chan int ,Sync_signal chan bool ) { defer func () { Sync_signal <- true }() for j:=2 ;j<i;j++{ if i%j == 0 { return } } ResultNum <- i return } func main () { Sync := make (chan bool ,100 ) ResultNum := make (chan int ,100 ) for i:=1 ;i<=100 ;i++{ go calc(i,ResultNum,Sync) } for i:=1 ;i<=100 ;i++{ <- Sync } close (ResultNum) for v := range (ResultNum){ fmt.Println(v) } }
使用range()
遍歷管道時,請注意管道必須先關閉 (close()
)否則panic
(Dead Lock)
result 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 2 1 7 3 5 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
example 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package mainimport "fmt" func sendData (country string , DataChan chan string , Signal chan struct {}) { DataChan <- country Signal <- struct {}{} } func getData (result chan string ) { for { country,ok := <-result if !ok { break } fmt.Println(country) } } func main () { country := []string {"Washington" ,"Tripoli" ,"London" ,"Beijing" ,"Tokyo" } DataChan := make (chan string ,len (country)) SyncChan := make (chan struct {},len (country)) for _,v := range country { go sendData(v,DataChan,SyncChan) } go func () { for i:=0 ;i<len (country);i++ { <- SyncChan } close (DataChan) }() getData(DataChan) }
result 1 2 3 4 5 Tripoli Washington Beijing London Tokyo
(補充)管道的只讀與只寫 通常不會用以下的方法直接創建,會直接拿來當參數的類型使用居多
於函數中接受的形式參數類型為 只讀(只寫) 的通道時,可傳入可讀寫的雙向通道作為實體參數 (推薦)
創建可讀可寫管道(一般) 1 2 3 var 變量名 = make (chan int ,1 )變量名 := make (chan int ,1 )
創建只讀 的管道 1 2 3 var 變量名 <- chan int 變量名 := make (<-chan int ,10 )
創建只寫 的管道 1 2 3 var 變量名 chan <- int 變量名 := make (chan <- int ,10 )
對channel進行select操作 應用1 管道都有固定大小, 實際業務開發中,有可能會有管道空間不夠使用的情況, 此時會一直阻塞,並等待數據被取出,才能再次寫入數據, 若是一直無法將數據取出,會造成之後要寫入的數據無法進入管道,而使得業務停擺
解決方式:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func main () { var testChan = make (chan int ,10 ) test2Chan := make (chan int ,10 ) for i:=1 ;i<25 ;i++{ select { case testChan <- i: case test2Chan <- i: default : fmt.Println(i,"can't send to Channel" ) } } }
result 1 2 3 4 21 can't send to Channel 22 can't send to Channel 23 can't send to Channel 24 can't send to Channel
應用2 管道可能為空的情況下, 有可能仍要持續的取出存放在管道內的數據, 否則會一直阻塞,等待管道中有數據被寫入,才能再次取出數據, 若是一直無數據被寫入,會造成業務停擺
解決方式:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" func main () { testChan := make (chan int , 10 ) for i:=1 ;i<10 ;i++{ testChan <- i } for i:=1 ;i<15 ;i++{ select { case v := <-testChan: fmt.Println("data" ,v ,"is got" ) default : fmt.Println("Channel have no item to get" ) } } }
result 1 2 3 4 5 6 7 8 9 10 11 12 13 14 data 1 is got data 2 is got data 3 is got data 4 is got data 5 is got data 6 is got data 7 is got data 8 is got data 9 is got Channel have no item to get Channel have no item to get Channel have no item to get Channel have no item to get Channel have no item to get