Go基礎系列 為select設定超時時間

2021-09-07 20:32:35 字數 2894 閱讀 2507

go channel系列

誰也無法保證某些情況下的select是否會永久阻塞。很多時候都需要設定一下select的超時時間,可以借助time包的after()實現。

time.after()的定義如下:

func after(d duration) <-chan time
after()函式接受乙個時長d,然後它after()等待d時長,等待時間到後,將等待完成時所處時間點寫入到channel中並返回這個唯讀channel。

所以,將該函式賦值給乙個變數時,這個變數是乙個唯讀channel,而channel是乙個指標型別的資料,所以它是乙個指標。

看下面的示例:

package main

import (

"fmt"

"time"

)func main()

輸出結果:

2018-11-20 19:05:11.5440307 +0800 cst m=+0.001994801

2018-11-20 19:05:12.5496378 +0800 cst m=+1.007601901

0xc042052060

如果將after()放進select語句塊的乙個case中,那麼就可以讓其它的case有一定的時間長度來監聽讀、寫事件,如果在這段時長內其它case還沒有有可讀、可寫事件,這個after()所在case就會結束當前的select,然後終止select(如果select未在迴圈中)或進入下一輪select(如果select在迴圈中)。

以下是乙個示例:

func main() ()

select

}

執行後,將在大約3秒之後輸出:

3 second over, timeover
上面出現了超時現象,因為新啟用的goroutine首先要等待5秒,然後才將資料傳送到channel ch1中。但是main goroutine繼續執行到select語句塊,由於第乙個case未滿足條件(注意,main goroutine並不會因此而阻塞)。評估第二個case時,將執行time.after()等待3秒,3秒之後讀取到該函式返回的通道資料,於是該case滿足select的條件,該select因為沒有在迴圈中,所以直接結束,main goroutine也因此而終止。自始至終,新啟用的goroutine都沒有機會將資料傳送到ch1中。

上面有兩個注意點:

如果將上面go func()函式的睡眠時間改為2秒,則在3秒等待時間內,第乙個case的<-ch1評估滿足條件,於是該case被選中,第二個case被無視。

go func() ()
上面使用after(),也保證了select一定會選中某乙個case,這時可以省略default塊。

注意,after()放在select的內部和放在select的外部是完全不一樣的,更助於理解的示例見下面的tick()。

after(d)是只等待一次d的時長,並在這次等待結束後將當前時間傳送到通道。tick(d)則是間隔地多次等待,每次等待d時長,並在每次間隔結束的時候將當前時間傳送到通道。

因為tick()也是在等待結束的時候傳送資料到通道,所以它的返回值是乙個channel,從這個channel中可讀取每次等待完時的時間點。

下面是乙個tick()和after()結合的示例:

package main

import (

"fmt"

"time"

)func main()

}

上面的示例,在等待2秒之後,就會因為讀取到了time.tick()的通道資料而終止,因為select並未在迴圈內。

如果select在迴圈內,第二個case將永遠選擇不到。因為每次select輪詢中,第乙個case都因為2秒而先被選中,使得第二個case的評估總是被中斷。進入下乙個select輪詢後,又會重新開始評估兩個case,分別等待2秒和7秒。

func main() 

}}

上面不正常執行的原因是因為每次select都會重新評估這些表示式。如果把這些表示式放在select外面,則正常:

package main

import (

"fmt"

"time"

)func main()

}}

返回:

start second: 9

1 second over: 10

1 second over: 11

1 second over: 12

1 second over: 13

1 second over: 14

1 second over: 15

1 second over: 16

7 second over: 16

將time.tick()和time.after()放在for...select的外面,使得select每次只評估通道是否可讀、可寫事件,而不會重新執行time.tick()和time.after(),使得它們重新進入計時狀態。

注意上面的輸出結果中,有兩行:

1 second over: 16

7 second over: 16

說明在第16秒的時候,兩個case都評估為真了,但是這一次選擇了第乙個case,然後進入下乙個select過程,因為select的隨機選擇性,它會保證所有滿足條件的case盡量均衡分布,這次將選擇第二個case,它仍然為第16秒,這時因為一次for和select呼叫所花的時間不可能會超過1秒而進入第17秒。

GO 開發系列 基礎 Go 併發程式設計

併發和並行說明 併發特點 並行特點 go 協程 說明 乙個 go 執行緒上可以起多個協程,協程可以理解為是輕量級的執行緒 go 協程特點 go 併發原理 mpg 模型 詳見部落格 示例 package main import fmt time 向 intchan放入 1 8000 個數 func p...

Go基礎系列 nil channel用法示例

go channel系列 當未為channel分配記憶體時,channel就是nil channel,例如var ch1 chan int。nil channel會永遠阻塞對該channel的讀 寫操作。nil channel會阻塞對該channel的所有讀 寫。所以,可以將某個channel設定為...

go語言 基礎系列 map基本操作

map 是雜湊表的引用,資料組 鍊錶的智慧型結合 建立 使用內建函式 mymap make map string int 通過字面量賦值 mymap map string int空map 為 map string int 刪除 使用內建函式delete delete mymap,a delete原型...