這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
註:該系列文章全部來自 Go By Example 系列翻譯而來,個人翻譯水平以及理解水平有限,如要更加精確的理解,請看原文Go by Example: Non-Blocking Channel Operations。
在 channels (通道?) 上基本的 sends (發送) 和 receives (接收)是阻塞模式的。儘管如此, 我們可以使用 select 和一個 default 子句來非阻塞的 sends、receives,甚至是非阻塞的多路選擇。
註:感謝@lidashuang的說明提醒,文章沒有描述清楚,修改如下:select預設是阻塞的,但在select裡面還有default文法,這類似於switch,default就是當監聽的channel都沒有準備好的時候,預設執行的(select不再阻塞等待channel)
同時,有時候會出現goroutine阻塞的情況,可以利用select設定逾時來避免整個程式進入阻塞狀態
代碼版本一
代碼如下:
package mainimport "fmt"func main() { messages := make(chan string) signals := make(chan bool) /**這裡是一個非阻塞 receive。如果在 messages 上的值是可用的,那 select 將 <-messages 的值帶上,執行 <-messages 下面的println語句。如果不是,它將立即帶上 default 的值,執行 default 下面的println語句**/ select { case msg := <-messages: fmt.Println("received message", msg) default: fmt.Println("no message received") }/**一個非阻塞 send 的類似工作 **/ msg := "hi" select { case messages <- msg: fmt.Println("sent message", msg) default: fmt.Println("no message sent") }/**我們可以用在 default 之上使用多個 cases 來實現一個非阻塞的多路 select。在這裡我們嘗試在 messages 和 signals 上實現非阻塞 receives。**/ select { case msg := <-messages: fmt.Println("received message", msg) case sig := <-signals: fmt.Println("received signal", sig) default: fmt.Println("no activity") }}
最後程式的結果輸出為:
$ go run non-blocking-channel-operations.go no message receivedno message sentno activity
代碼版本二
代碼如下:
package mainimport ( "fmt")func main() { // messages := make(chan string) //如果不加緩衝的話,就全部會選擇defalut messages := make(chan string, 1) //加了緩衝的話,會選擇對應的 signals := make(chan bool) // messages <- "test" select { case msg := <-messages: fmt.Println("received message", msg) //因為messages目前本身還沒有值,因此選擇default執行 default: fmt.Println("no message received") } // go func() { msg := "hi world" // }() select { case messages <- msg: fmt.Println("sent message", msg) //因為channels有緩衝,所以這裡的msg發送到 channels messages 能處理,不會被阻塞住 default: fmt.Println("no message sent") } select { case msg := <-messages: fmt.Println("received message", msg) //因為messages已經有值了,所以會選擇這個case執行 case sig := <-signals: fmt.Println("received signal", sig) default: fmt.Println("no activity") }}
輸出結果如下:
$ go run non-blocking-channel-operations.go no message receivedsent message hi worldreceived message hi world
代碼版本三
代碼如下:
package mainimport ( "fmt")func main() { // messages := make(chan string) //如果不加緩衝的話,就全部會選擇defalut messages := make(chan string, 1) //加了緩衝的話,會選擇對應的 signals := make(chan bool) messages <- "test" select { case msg := <-messages: fmt.Println("received message", msg) //messages已經有值,因此選擇這個case執行 default: fmt.Println("no message received") } // go func() { msg := "hi world" // }() 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") }}
代碼輸出結果如下:
$ go run non-blocking-channel-operations.go received message testsent message hi worldreceived message hi world
代碼版本四
代碼如下:
package mainimport ( "fmt")func main() { messages := make(chan string) //如果不加緩衝的話,就全部會選擇defalut //messages := make(chan string, 1) //加了緩衝的話,會選擇對應的 signals := make(chan bool) messages <- "test" //因為該channels沒有緩衝,而其又賦值了,會到時死結。 select { case msg := <-messages: fmt.Println("received message", msg) default: fmt.Println("no message received") } // go func() { msg := "hi world" // }() 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") }}
代碼輸出結果為:
fatal error: all goroutines are asleep - deadlock!
runtime goroutine
runtime包中有幾個處理goroutine的函數
- Goexit - 退出當前執行的goroutine,但是defer函數還會繼續調用
- Gosched - 讓出當前goroutine的執行許可權,調度器安排其它等待的任務運行,並在下次某個時候從該位置恢複執行
- NumCPU - 返回CPU核心數量
- NumGoroutine - 返回正在執行和排隊的任務總數
- GOMAXPROCS - 用來設定可以啟動並執行CPU核心數