這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
概述
為了並發的goroutines之間的通訊,golang使用了管道channel。你可以通過一個goroutines向channel發送資料,然後從另一個goroutine接收它
通常我們會使用make來建立channel ----- make(chan valType , [size])。
寫入 channel c<-
讀取channel <-c
分類
一 無緩衝
make建立時忽略第二個參數即可建立無緩衝channel執行個體如下:
// csdnTest.gopackage mainimport ("fmt" "time")func main() {messages := make(chan string)go func() {messages <- "hello"}()msg := <-messages fmt.Println(msg)}
值得注意的是:無緩衝channel只有接收和發送方都準備好了,channel才能正常工作,上述代碼中如果我們把msg := <-messages這句注釋掉,那麼messages <- "hello"將會一直阻塞。
在這裡做個猜想: 無緩衝的buffer只是直使用了ipc的pipe,pipe兩端都開啟時才能使用。“<-”被解釋為開啟寫端,“->被解釋為開啟讀端”
二 有緩衝
樣本:
// csdnTest.gopackage mainimport ("fmt")func main() {messages := make(chan string, 2)messages <- "hello"messages <- "world"fmt.Println(<-msg)fmt.Println(<-msg)}
說明:當建立有緩衝的buffer時,就不需要有準備好的接收器來接受資料,資料可以存在channel的緩衝區中,有緩衝的channel有如下特性:
1:資料未滿可以寫,當資料滿了再寫阻塞。
2:資料未空可以讀,當資料空了再讀阻塞。
通過上面的特性可以做個大膽的猜測,有緩衝的是通過msg-queue來實現的。
有緩衝和無緩衝從底層的實現就不同,因為他們使用了不同的ipc。
三 非阻塞
正常情況下的channel讀和寫都是阻塞的,然而我們可以使用一個帶有default的select來觸發非阻塞的channle。原本select也是阻塞的,在加入default時select本身也變為非阻塞
舉個例子:
// csdnTest.gopackage mainimport "fmt"func main() {messages := make(chan string)signals := make(chan bool)//message和signals是無緩衝channel,因為接收器和發送器沒同時準備好,所以channel無法使用。//下面所有select會輸出default的內容select {case msg := <-messages:fmt.Println("received message", msg)default:fmt.Println("no message received")}msg := "hi"select {case messages <- msg:fmt.Println("sent message", msg)default:fmt.Println("no message sent")}select {case msg := <-messages:fmt.Println("received message", msg)case sig := <-signals:fmt.Println("received signal", sig)default:fmt.Println("no activity")}}
有興趣同學將上面無緩衝的改成有緩衝的嘗試一下。
至於select為何在加入default後channel和自身都未非阻塞了,你可以去看看go源碼select.go這個源檔案,-------------------也可以閱讀我的下一期部落格。
四 channel的用例
4.1 處理逾時
c := make(chan string, 1) go func() { time.Sleep(time.Second * 3) c <- "inner goroutine" }() select { case res := <-c: fmt.Println(res) case <-time.After(time.Second * 1): fmt.Println("timeout") }
4.2 channel關閉
關閉一個channel時,就不能向該channel中寫資料了。通常我們用一個值傳回值來接收channel的資料,但此時可以用兩個傳回值 (valeu,b:=<-c) 來捕獲channel是否關閉 b取值false或true。
當我們處理一個需要結束的任務,可採用下面的方式來調度
jobs := make(chan int, 10)done := make(chan bool)go func() {for {j, more := <-jobsif more {fmt.Println("received job", j)} else {fmt.Println("received all jobs")done <- truereturn}}}()for j := 1; j <= 3; j++ {jobs <- jfmt.Println("sent job", j)}close(jobs)
4.3 資料乒乓球式的流轉
舉一個golang官網比較知名的一個例子:
package mainimport "fmt"func ping(pings chan<- string, msg string) { pings <- msg}func pong(pings <-chan string, pongs chan<- string) { msg := <-pings pongs <- msg}func main() { pings := make(chan string, 1) pongs := make(chan string, 1) ping(pings, "passed message") pong(pings, pongs) fmt.Println(<-pongs)}
謝謝各位的閱讀