問題
情況
我目前有一個 gin 處理函式,它使用相同的背景關系在三個單獨的 goroutine 中運行三個單獨的查詢。有一個錯誤組 ( "golang.org/x/sync/errgroup") 使用此共享背景關系,并且處理程式在回傳之前等待錯誤組。
客觀的
我試圖實作的行為是在其中一個 goroutine 完成后,應該對剩余的 goroutine 強制執行超時,但如果 gin 請求被取消(連接關閉),這個背景關系也應該被取消,這意味著 ginctx.Request.Context()必須使用。
潛在的解決方案
當前實施
目前,我有一個將超時傳遞給 errgroup 的背景關系,但這只是對所有 goroutine 強制執行超時。
timeoutCtx := context.WithTimeout(context.Background(), 10*time.Second)
g, err := errgroup.WithContext(timeoutCtx)
g.Go(func1)
g.Go(func2)
g.Go(func3)
err = g.Wait()
需要使用 gin 請求背景關系,這樣如果連接關閉并且請求被取消,goroutines 也會停止。
// ctx *gin.Context
g, err := errgroup.WithContext(ctx.Request.Context())
g.Go(func1)
g.Go(func2)
g.Go(func3)
err = g.Wait()
使用通道實作超時
來源
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string, 1)
go func() {
time.Sleep(2 * time.Second)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <-time.After(1 * time.Second):
fmt.Println("timeout 1")
}
c2 := make(chan string, 1)
go func() {
time.Sleep(2 * time.Second)
c2 <- "result 2"
}()
select {
case res := <-c2:
fmt.Println(res)
case <-time.After(3 * time.Second):
fmt.Println("timeout 2")
}
}
結合渠道和請求背景關系
這個解決方案很接近,但不是很優雅或不完整。
cQueryDone := make(chan bool)
g, err := errgroup.WithContext(ctx.Request.Context())
g.Go(func1)
g.Go(func2)
g.Go(func3)
// assumes func1 func2 and func3 all have cQueryDone <- true
if <-cQueryDone {
select {
case <-cQueryDone:
select {
case <-cQueryDone:
// ctx.JSON
// return
case <-time.After(1*time.Second):
// ctx.JSON
// return
}
case <-time.After(3*time.Second):
// ctx.JSON
// return
}
}
err = g.Wait()
在 Go 中是否有更好、更慣用的方法來實作這種行為?
uj5u.com熱心網友回復:
請注意context.WithTimeout():
- 可以包裝任何背景關系(不僅僅是
context.Background()) - 還回傳一個
cancel函式
您可以在上面添加超時ctx.Request.Context(),并cancel在任何查詢完成時呼叫:
timeoutCtx, cancel := context.WithTimeout(ctx.Request.Context())
g, err := errgroup.WithContext(timeoutCtx)
g.Go( func1(cancel) ) // pass the cancel callback to each query some way or another
g.Go( func2(cancel) ) // you prabably want to also pass timeoutCtx
g.Go( func3(cancel) )
g.Wait()
根據您的評論:還有context.WithCancel(),您可以在延遲后呼叫取消
childCtx, cancel := context.WithCancel(ctx.Request.Context())
g, err := errgroup.WithContext(childCtx)
hammerTime := func(){
<-time.After(1*time.Second)
cancel()
}
g.Go( func1(hammerTime) ) // funcXX should have access to hammerTime
g.Go( func2(hammerTime) )
g.Go( func3(hammerTime) )
g.Wait()
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/471680.html
上一篇:顫動為什么不異步和等待作業?
