亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

goroutine 沒有看到上下文取消?

goroutine 沒有看到上下文取消?

Go
茅侃侃 2022-12-19 21:06:55
我有兩個 goroutines 同時運行。在某些時候,我希望我的程序正常退出,所以我使用cancel()func 通知我的 goroutines 它們需要停止,但只有兩個中的一個收到消息。這是我的主要(簡化):ctx := context.Background()ctx, cancel := context.WithCancel(ctx)done := make(chan os.Signal, 1)signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)wg := &sync.WaitGroup{}wg.Add(2)go func() {    err := eng.Watcher(ctx, wg)    if err != nil {        ancel()    }}()go func() {    err := eng.Suspender(ctx, wg)    if err != nil {        cancel()    }}()<-done // wait for SIGINT / SIGTERMlog.Print("receive shutdown")cancel()wg.Wait()log.Print("controller exited properly")Suspender goroutine 成功存在(這里是代碼):package mainimport (    "context"    "sync"    "time"    log "github.com/sirupsen/logrus"    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"    "k8s.io/client-go/util/retry")func (eng *Engine) Suspender(ctx context.Context, wg *sync.WaitGroup) error {    contextLogger := eng.logger.WithFields(log.Fields{        "go-routine": "Suspender",    })    contextLogger.Info("starting Suspender goroutine")    now := time.Now().In(eng.loc)    for {        select {        case n := <-eng.Wl:            //dostuff        case <-ctx.Done():            // The context is over, stop processing results            contextLogger.Infof("goroutine Suspender canceled by context")            return nil        }    }}這是未接收上下文取消的函數:package mainimport (    "context"    "sync"    "time"    log "github.com/sirupsen/logrus")func (eng *Engine) Watcher(ctx context.Context, wg *sync.WaitGroup) error {    contextLogger := eng.logger.WithFields(log.Fields{        "go-routine":      "Watcher",        "uptime-schedule": eng.upTimeSchedule,    })    contextLogger.Info("starting Watcher goroutine")    你能幫我么 ?
查看完整描述

3 回答

?
DIEA

TA貢獻1820條經驗 獲得超2個贊

你用錯誤組試過嗎?它內置了上下文取消功能:


ctx := context.Background()

ctx, cancel := context.WithCancel(ctx)

defer cancel()


done := make(chan os.Signal, 1)

signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)


// "golang.org/x/sync/errgroup"

wg, ctx := errgroup.WithContext(ctx)


wg.Go(func() error {

    return eng.Watcher(ctx, wg)

})


wg.Go(func() error {

    return eng.Suspender(ctx, wg)

})


wg.Go(func() error {

    defer cancel()

    <-done

    return nil

})


err := wg.Wait()

if err != nil {

    log.Print(err)

}


log.Print("receive shutdown")

log.Print("controller exited properly")


查看完整回答
反對 回復 2022-12-19
?
瀟湘沐

TA貢獻1816條經驗 獲得超6個贊

Suspenderin和 in中的代碼Watcher不會通過Done()方法調用遞減等待組計數器 - 無限執行背后的原因。


老實說,忘記這樣的小事是很正常的。這就是為什么作為 Go 中的標準一般做法,建議在一開始就使用defer和處理關鍵的事情(并且應該在 function/method 內部處理)。


更新后的實現可能看起來像


func (eng *Engine) Suspender(ctx context.Context, wg *sync.WaitGroup) error {

    defer wg.Done()


// ------------------------------------


func (eng *Engine) Watcher(ctx context.Context, wg *sync.WaitGroup) error {

    defer wg.Done()

    contextLogger := eng.logger.WithFields(log.Fields{


另外,另一個建議是查看主例程,總是建議傳遞context by value給正在調用的任何 go-routine 或方法調用 (lambda)。這種方法使開發人員免于遇到許多不易被發現的與程序相關的錯誤。


go func(ctx context.Context) {

    err := eng.Watcher(ctx, wg)

    if err != nil {

        cancel()

    }

}(ctx)

Edit-1:(精確解)


如前所述,嘗試使用 go 例程中的值傳遞上下文。否則,兩個 go 例程都將使用單個上下文(因為您正在引用它)并且只會ctx.Done()觸發一個。通過ctx作為值傳遞,在 Go 中創建了 2 個單獨的子上下文。在使用 cancel() 關閉父級時 - 兩個子級獨立觸發ctx.Done()。


查看完整回答
反對 回復 2022-12-19
?
慕少森

TA貢獻2019條經驗 獲得超9個贊

從表面上看,代碼看起來不錯。唯一能想到的就是它忙于“dostuff”。在調試器中逐步執行與計時相關的代碼可能會很棘手,因此請嘗試添加一些日志記錄:


  case <-ticker.C:

     log.Println("doing stuff")

     //dostuff

     log.Println("done stuff")

(我還假設您正在某處調用wg.Done()您的 go-routines,但如果它們丟失,那不會是您描述的問題的原因。)


查看完整回答
反對 回復 2022-12-19
  • 3 回答
  • 0 關注
  • 134 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號