golang關鍵字select的三個例子, time.After模擬socket/心跳超時
例子1 select會隨機選擇一個可執行的case
// 這個例子主要說明select是隨機選擇一個可執行的case
func main() {
// 定義兩個chan, 用於儲存資料
// 由於我打算提前把資料存進去, 所以定義緩衝區為10
chan1 := make(chan int, 10)
chan2 := make(chan int, 10)
// 在兩個chan中都放入10個資料
for i := 0; i < 10; i++ {
chan1 <- i
}
for i := 0; i < 10; i++ {
chan2 <- i
}
// 啟動一個for迴圈, 無限迴圈
// select的作用:
// select會隨機選擇一個case, 判斷該case後面的語句是否可以執行
// 如果可以執行, 進入case的程式碼塊中, 執行程式碼塊
// 如果不能只能, 則隨機挑選另一個case來進行判斷
// select會持續這個隨機挑選判斷的過程, 直到某一個case可以被執行, 並且執行它
// 也就是說select會阻塞在這裡, 直到選擇了某一個case
// 特別說明: 判斷的順序是隨機的, 看看第一個, 看看第二個, 再看看第二個, 再看看第一個
// 這個和switch-case是不一樣的
fmt.Println("即將進入select")
fmt.Println("你會發現chan 1和chan 2是隨機列印的, 沒有規 ")
fmt.Println("驗證了select是隨機選擇")
for {
select {
case num1 := <-chan1:
{
fmt.Printf("從chan 1中取出了資料, 這是第%d次選擇case 1\n", num1)
}
case num2 := <-chan2:
{
fmt.Printf("從chan 2中取出了資料, 這是第%d次選擇case 2\n", num2)
}
case <-time.After(time.Second * 5):
{
// 請忽略這個case, 僅僅是為了退出程式
// time.After的用法將在case 2中講解
fmt.Println("5秒了, 程式退出")
return
}
}
}
}
例子2 select會阻塞, 直到某一個case可以執行 && time.After的用法
// 這個例子主要驗證select會阻塞, 直到某一個case可以執行
// 同時也會說明time.After的用法
func main() {
// 建立一個通道, 在for迴圈中會從中取值
dataChan := make(chan int)
// 啟動一個go程
go func() {
// 13秒之後, 我往這個dataChan中放入一個資料
// 這個資料是什麼不重要, 重要的是我放入了一個資料
time.Sleep(time.Second * 13)
fmt.Println("13秒到了, 往dataChan放入一個資料")
dataChan <- 1
}()
// 這個go程只是用來列印時間和退出程式的, 對測試內容沒有任何影響
go func() {
for i := 0; ; i++ {
fmt.Printf("%d秒\n", i+1)
time.Sleep(time.Second * 1)
if i == 22 {
fmt.Println("測試結束, 程式退出")
os.Exit(0)
}
}
}()
// 啟動一個for迴圈, 無限迴圈
for {
// 每次迴圈從這裡開始
fmt.Println("-----我是select, 我開始阻塞選擇啦-----")
select {
case <-dataChan:
{
// 判斷是否能從dataChan這個channel中取出資料來
// 顯然前13秒是取不出來的, 因為沒有人給他放資料
fmt.Println("13秒到了, 從dataChan中取出了資料")
fmt.Println("注意觀察, 下一次select的時候, time.After會重新計時5秒")
}
case <-time.After(time.Second * 5):
// time.After的作用:
// 每次執行到select時
// 這個time.After會開始計時
// (如果到了)5秒後, 就會生產出一個資料, select就會認為這個case可以執行, 選擇這個case
// 如果沒有到5秒, select就選擇了其他case, 那麼這個計時器會清零
// 下一次迴圈中, 會重新進行5秒計時
fmt.Println("5秒過去啦, time.After生產出了資料, select選擇了第二個case")
}
}
}
例子3 用select來判斷socket/心跳超時
// 這個例子是用select來判斷socket/心跳超時
func main() {
// 用這個channel來模擬socket輸入, 輸入資料或者心跳
socketChan := make(chan int)
go func() {
// 模擬輸入/心跳, 每5秒輸入一個資料/接收到心跳
for i := 0; i < 2; i++ {
time.Sleep(time.Second * 5)
fmt.Printf("socket: 第%d個5秒到了, 我輸入一個資料\n", i+1)
socketChan <- i
}
// 10秒後, socket崩潰, 不再輸入資料/不再傳送心跳
time.Sleep(time.Millisecond * 1)
fmt.Println("socket: 我崩潰啦, 沒有辦法繼續輸入資料了")
fmt.Println("開始計算超時時間, 預計10秒後超時")
}()
// 這個go程只是用來列印時間和退出程式的, 對測試內容沒有任何影響
go func() {
for i := 0; ; i++ {
fmt.Printf("%d秒\n", i+1)
time.Sleep(time.Second * 1)
if i == 21 {
fmt.Println("測試結束, 程式退出")
os.Exit(0)
}
}
}()
// 啟動一個for迴圈, 無限迴圈
for {
fmt.Println("-----我是select, 我開始阻塞選擇啦-----")
select {
case num := <-socketChan:
{
fmt.Printf("我是select: 我收到了socket輸入 是第%d次\n", num+1)
}
case <-time.After(time.Second * 10):
{
// 超時時間設定為10秒
fmt.Println("我是select: 連續10秒都沒有收到socket輸入, 超時了")
return
}
}
}
}