前沿
go語言沒有提供直接的超時處理機制,所謂超時可以理解爲我們上網瀏覽一些網站時,如果一段時間之後不操作,就會需要重新登錄;那麼我們應該如何實現這一功能呢?這時就需要select來設置超時。
雖然selecct機制不是專門爲超時而設計的,卻能很方便的解決超時問題,因爲select的特點是隻要其中一個case已經完成,程序就會繼續往下執行,而不會考慮其他的case的情況。
超時機制本身雖然也會帶來一些問題,比如在運行比較快的機器或者高速的網絡上運行正常的程序,到了慢速的機器或者網絡上運行就會出現問題,從而出現結果不一致的現象,但從根本上來說解決死鎖的價值要遠大於所帶來的的問題。
select的用法與swith語言非常類似,由select開始一個新的選擇塊,每個選擇條件由case語句來描述。
與switch語句相比,select有比較多的限制,其中最大的一條限制就是每個case語句裏必須是一個IO操作,大致結構如下:
select {
case <-chan1:
//如果chan1成功讀取到數據,則進行case處理語句
case chan2<-1:
//如果成功向chan2寫入數據則進行該case處理語句
default:
//如果上面的都沒有成功,則進入default處理流程
}
在一個select語句中,go語言會按照順序從頭至尾評估每一個發送和接收的語句。如果其中的任意一語句可以繼續執行(即沒有被阻塞),那麼就從那些可以執行的語句中任意選擇一條來使用。果沒有任意一條語句可以執行(即所有通道都被阻塞),那麼有如下兩種可能的情況:
- 如果給出了default語句,那麼就會執行default語句,同時程序的執行會從select語句後的語句中恢復;
- 如果沒有default語句,那麼select語句將被阻塞,直到至少有一個通信可以進行下去。
示例代碼:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新開的一個協程
go func() {
for {
select {
case num := <-ch:
fmt.Println("Num =", num)
case <-time.After(3 * time.Second):
fmt.Println("超時")
quit <- true
}
}
}() //別忘了()
for i := 0; i < 5; i++ {
ch <- i
time.Sleep(time.Second)
}
<-quit
fmt.Println("程序結束")
}
輸出結果:
Num = 0
Num = 1
Num = 2
Num = 3
Num = 4
超時
程序結束