Context 超時處理應該這麼做

1、超時處理有問題代碼

只通過 select 監聽超時信號,沒有處理正常邏輯處理。

package main

import (
 "context"
 "fmt"
 "sync"
 "time"
)

var wg sync.WaitGroup

func main() {
 // 定義一個3s超時context 所有衍生出來的goroutine必須在3s內完成 否則會cancel掉
 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
 defer cancel()

 go func() {
  wg.Add(2)
  go watch(ctx, 1)
  go watch(ctx, 2)
  wg.Wait()
 }()

// 這裏有問題,因爲不管是超時還是未超時都會進入<-ctx.Done() 也就是都會等待3s 這不是我們想要的
 select {
 case <-ctx.Done():
  fmt.Printf("watch %d %s\n", 0, ctx.Err())
 }

 fmt.Println("finished")
}

func watch(ctx context.Context, flag int) {
 defer wg.Done()

 func() {
  fmt.Printf("doing something flag:%d\n", flag)
  time.Sleep(10*time.Second)
  fmt.Println("finished flag:", flag)
 }()
}

2、如何解決?

很簡單,就是在所有 goroutine 結束之後發送一個 done 信號給主 goroutine,這樣 select 就會監聽到這個信號,從而安全退出。

// 增加done信號
 done := make(chan struct{})
 go func() {
  wg.Add(2)
  go watch(ctx, 1)
  go watch(ctx, 2)
  wg.Wait()
  done <- struct{}{} // 發送done信號
 }()

 select {
 case <-done: // 接受done信號
  fmt.Println("Done")
 case <-ctx.Done():
  fmt.Printf("watch %d %s\n", 0, ctx.Err())
 }
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/fmvxsXgnLm2bS3qaqZ-JLA