使用兩個context實現CLOSE包的超時等待
在UDP中,一般傳送者傳送包後,如果一定的時間對方沒有收到,就需要重傳。例如UDP實現握手的過程,如果握手的包,比如RTMFP協議的IHELLO,傳送給對方後,如果一定1秒沒有收到,就應該重發一次,然後等3秒、6秒、9秒,如果最後沒有收到就是超時了。
最後一個Close包,傳送者不能等待這麼長的時間,所以需要設定一個較短的時間做超時退出。一般收發都是一個context,在最後這個Close包時,收到ctx.Done也不能立刻退出,因為還需要稍微等待,譬如600毫秒如果沒有收到響應才能退出。
一個可能的實現是這樣:
in := make(chan []byte)
func Close(ctx context.Context) (err error) {
timeous := ... // 1s,3s,6s,9s...
for _, to := range timeouts {
// 傳送給對方WriteToUDP("CLOSE", peer)
// 另外一個goroutine讀取UDP包到in
select {
case <- time.After(to):
case <- in:
fmt.Println("Close ok")
return
case <- ctx.Done():
fmt.Println("Program quit")
return
}
}
return
}
但是這個問題在於,在程式退出時,一般都會cancel ctx然後呼叫Close方法,這個地方就不會等待任何的超時,就列印"Program quit"然後返回了。解決方案是用另外一個context。但是如何處理之前的ctx的done呢?可以再起一個goroutine做同步:
in := make(chan []byte)
func Close(ctx context.Context) (err error) {
ctxRead,cancelRead := context.WithCancel(context.Background())
go func(){ // sync ctx with ctxRead
select {
case <-ctxRead.Done():
case <-ctx.Done():
select {
case <-ctxRead.Done():
case <-time.After(600*time.Milliseconds):
cancelRead()
}
}
}()
ctx = ctxRead // 下面直接用ctxRead。
timeous := ... // 1s,3s,6s,9s...
for _, to := range timeouts {
// 傳送給對方WriteToUDP("CLOSE", peer)
// 另外一個goroutine讀取UDP包到in
select {
case <- time.After(to):
case <- in:
fmt.Println("Close ok")
return
case <- ctx.Done():
fmt.Println("Program quit")
return
}
}
return
}
這樣在主要的邏輯中,還是隻需要處理ctx,但是這個ctx已經是新的context了。不過在實際的過程中,這個sync的goroutine需要確定起來後,才能繼續,否則會造成執行順序不確定:
sc := make(chan bool, 1)
go func(){ // sync ctx with ctxRead
sc <- true
select {
......
}
<- sc
使用context,來控制多個goroutine的執行和取消,是非常好用的,關鍵可以完全關注業務的邏輯,而不會引入因為ctx取消或者超時機制而造成的特殊邏輯。
相關文章
- GOLANG使用Context實現傳值、超時和取消GolangContext
- 兩個重要的等待事件!事件
- RDSforMySQLInnoDB行鎖等待和鎖等待超時的處理ORMMySql
- 【原】兩個時間相加的運算子過載實現
- 《Lua-in-ConTeXt》03:兩個世界Context
- 測試 iris 時自定義 context 包Context
- 介面呼叫超時的實現原理
- context包Context
- Go 語言 context 包實踐GoContext
- JAVA 兩個類同時實現同一個介面的方法Java
- Context真正的實現與Context設計模式Context設計模式
- JavaScript實現兩個數的交換JavaScript
- golang中的context包GolangContext
- 使用 nodejs 中的 http 模組實現幾個超實用的工具NodeJSHTTP
- Go:context包GoContext
- 使用require.context實現優雅的預載入UIContext
- ruby webdriver 顯性等待、隱性等待、內部超時處理Web
- 使用純粹的ABAP位操作實現兩個整數相加
- 兩個棧實現佇列佇列
- Seata 全域性鎖等待超時 問題排查
- ORA-04021: 等待物件鎖超時物件
- java實現兩個文字相似度 simHash 實現Java
- 分享兩個超可愛的屏保
- 用 WebSphere CloudBurst 實現定製: 使用指令碼包定製超級模式WebCloud指令碼模式
- RabbitMQ實現延時訊息的兩種方法MQ
- Spring定時器的兩種實現方式Spring定時器
- 使用Trigger實現兩個Table同步更新資料
- pandas 實現兩個dataframe相減的方式
- EventBus實現兩個Fragment直接的跳轉Fragment
- context裡的超時時間是怎麼在微服務之間傳遞的Context微服務
- 怎樣實現一個非阻塞的超時重試任務佇列佇列
- 關於使用 Vue 實現 Context-Menu 的思考與總結VueContext
- 故障分析 | MySQL鎖等待超時一例分析MySql
- 兩個棧實現佇列操作佇列
- [ Shell ] 兩個 case 實現 GetOptions 效果
- 用兩個棧實現佇列佇列
- windows上使用telnet時遇到的兩個錯誤Windows
- NIO中如何實現超時重傳?