在Go語言中,通道是goroutine與另一個goroutine通信的媒介,并且這種通信是無鎖的。換句話說,通道是一種技術(shù),它允許一個goroutine將數(shù)據(jù)發(fā)送到另一個goroutine。默認(rèn)情況下,通道是雙向的,這意味著goroutine可以通過同一通道發(fā)送或接收數(shù)據(jù),如下圖所示:
在Go語言中,使用chan關(guān)鍵字創(chuàng)建通道,并且該通道只能傳輸相同類型的數(shù)據(jù),不允許從同一通道傳輸不同類型的數(shù)據(jù)。
語法:
var Channel_name chan Type
您還可以使用簡寫聲明通過make()函數(shù)創(chuàng)建通道。
語法:
channel_name:= make(chan Type)
package main import "fmt" func main() { //使用var關(guān)鍵字創(chuàng)建通道 var mychannel chan int fmt.Println("channel的值: ", mychannel) fmt.Printf("channel的類型: %T ", mychannel) // 使用 make() 函數(shù)創(chuàng)建通道 mychannel1 := make(chan int) fmt.Println("\nchannel1的值:", mychannel1) fmt.Printf("channel1的類型: %T ", mychannel1) }
輸出:
channel的值: <nil> channel的類型: chan int channel1的值: 0xc0000160c0 channel1的類型: chan int
在Go語言中,通道工作有兩個主要的操作,一個是發(fā)送,另一個是接收,這兩個操作統(tǒng)稱為通信。<-運(yùn)算符的方向表示是接收數(shù)據(jù)還是發(fā)送數(shù)據(jù)。在通道中,默認(rèn)情況下,發(fā)送和接收操作塊直到另一端沒有數(shù)據(jù)為止。它允許goroutine在沒有顯式鎖或條件變量的情況下彼此同步。
發(fā)送操作:發(fā)送操作用于在通道的幫助下將數(shù)據(jù)從一個goroutine發(fā)送到另一個goroutine。像int,float64和bool之類的值可以安全且容易地通過通道發(fā)送,因?yàn)樗鼈兪潜粡?fù)制的,因此不存在意外并發(fā)訪問相同值的風(fēng)險。同樣,字符串也是安全的,因?yàn)樗鼈兪遣豢勺兊?。但是,通過通道發(fā)送指針或引用(例如切片,map集合等)并不安全,因?yàn)橹羔樆蛞玫闹悼赡軙ㄟ^同時發(fā)送goroutine或接收goroutine更改,并且結(jié)果無法預(yù)測。因此,在通道中使用指針或引用時,必須確保它們一次只能由一個goroutine訪問。
Mychannel <- element
上面的語句表明數(shù)據(jù)(element)在<-運(yùn)算符的幫助下發(fā)送到通道(Mychannel)。
接收操作:接收操作用于接收發(fā)送操作方發(fā)送的數(shù)據(jù)。
element := <-Mychannel
上面的語句表明該元素從channel(Mychannel)接收數(shù)據(jù)。如果接收到的語句的結(jié)果不可用(不需要使用),則也是有效的語句。您還可以編寫如下的receive語句:
<-Mychannel
package main import "fmt" func myfunc(ch chan int) { fmt.Println(234 + <-ch) } func main() { fmt.Println("主方法開始") //創(chuàng)建通道l ch := make(chan int) go myfunc(ch) ch <- 23 fmt.Println("主方法結(jié)束") }
輸出:
主方法開始 257 主方法結(jié)束
您也可以在close()函數(shù)的幫助下關(guān)閉通道。這是一個內(nèi)置函數(shù),并設(shè)置一個標(biāo)識,表示不再有任何值將發(fā)送到該通道。
語法:
close()
您也可以使用for范圍循環(huán)關(guān)閉通道。在這里,接收器goroutine可以借助給定的語法檢查通道是打開還是關(guān)閉:
ele, ok:= <- Mychannel
在此,如果ok的值為true,則表示通道已打開,因此可以執(zhí)行讀取操作。并且,如果的值為false,則表示該通道已關(guān)閉,因此將不執(zhí)行讀取操作。
//Go程序說明如何 //關(guān)閉使用的通道 //range循環(huán)和關(guān)閉函數(shù) package main import "fmt" func myfun(mychnl chan string) { for v := 0; v < 4; v++ { mychnl <- "nhooo" } close(mychnl) } func main() { //創(chuàng)建通道 c := make(chan string) // 使用 Goroutine go myfun(c) //當(dāng)ok的值為為true時,表示通道已打開,可以發(fā)送或接收數(shù)據(jù) //當(dāng)ok的值設(shè)置為false時,表示通道已關(guān)閉 for { res, ok := <-c if ok == false { fmt.Println("通道關(guān)閉 ", ok) break } fmt.Println("通道打開 ", res, ok) } }
輸出:
通道打開 nhooo true 通道打開 nhooo true 通道打開 nhooo true 通道打開 nhooo true 通道關(guān)閉 false
阻止發(fā)送和接收:在通道中,當(dāng)數(shù)據(jù)發(fā)送到通道時,控制在發(fā)送語句中被阻塞,直到其他goroutine從該通道讀取數(shù)據(jù)。類似地,當(dāng)通道從goroutine接收數(shù)據(jù)時,read語句塊直到另一條goroutine語句。
零值通道:通道的零值為nil。
通道中的For循環(huán): for循環(huán)可以遍歷通道上發(fā)送的順序值,直到關(guān)閉為止。
語法:
for item := range Chnl { // 語句.. }
package main import "fmt" func main() { // 使用 make() 函數(shù)創(chuàng)建通道 mychnl := make(chan string) // 匿名 goroutine go func() { mychnl <- "GFG" mychnl <- "gfg" mychnl <- "Geeks" mychnl <- "nhooo" close(mychnl) }() //使用for循環(huán) for res := range mychnl { fmt.Println(res) } }
輸出:
GFG gfg Geeks nhooo
通道的長度:在通道中,您可以使用len()函數(shù)找到通道的長度。在此,長度表示在通道緩沖區(qū)中排隊的值的數(shù)量。
package main import "fmt" func main() { // 使用 make() 函數(shù)創(chuàng)建通道 mychnl := make(chan string, 4) mychnl <- "GFG" mychnl <- "gfg" mychnl <- "Geeks" mychnl <- "nhooo" // 使用 len() 函數(shù)查找通道的長度 fmt.Println("channel長度為: ", len(mychnl)) }
輸出:
channel長度為: 4
通道的容量:在通道中,您可以使用cap()函數(shù)找到通道的容量。在此,容量表示緩沖區(qū)的大小。
package main import "fmt" func main() { // 使用 make() 函數(shù)創(chuàng)建通道 mychnl := make(chan string, 4) mychnl <- "GFG" mychnl <- "gfg" mychnl <- "Geeks" mychnl <- "nhooo" // 使用 cap() 函數(shù)查找通道的容量 fmt.Println("channel容量為: ", cap(mychnl)) }
輸出:
channel容量為: 5
Channel中的Select和case語句:在go語言中,select語句就像沒有任何輸入?yún)?shù)的switch語句。在通道中使用select語句從case塊提供的多個操作中執(zhí)行單個操作。