go的context.WithTimeout运用

本文详细探讨了Go语言中context.WithTimeout的使用,通过普通和奇怪示例展示了在不同场景下的行为。文章提出了在父环境和子环境设置不同超时时遇到的问题,并提供了多个解决方案,包括糟糕方案和有效方案,强调了主协程退出对子协程的影响。
摘要由CSDN通过智能技术生成

context.WithTimeout

看着简单,复杂环境下用着难

普通示例1

func Test_CancelTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(5))
	defer cancel()
	fmt.Println("main=>", time.Now())
	go worker()
	for range ctx.Done() {
		fmt.Println("main receive notice", time.Now())
		break
	}
	fmt.Println("main<=", time.Now()) // worker()超过了5s,但是依旧5s后退出
}

func worker() {
	fmt.Println("worker=>", time.Now())
	i := 0
	for {
		i++
		if i > 100000000 {
			fmt.Println("worker run", time.Now())
			i = 0
		}
	}
	fmt.Println("worker<=", time.Now())
}

普通示例2

func Test_CancelTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(5))
	defer cancel()
	fmt.Println("main=>", time.Now())
	go worker()
	for range ctx.Done() {
		fmt.Println("main receive notice", time.Now())
		break
	}
	fmt.Println("main<=", time.Now()) // worker()没有超过5s,但是依旧5s后退出
}

func worker() {
	fmt.Println("worker=>", time.Now())
	i := 0
	for {
		i++
		if i > 100 {
			break
		}
	}
	fmt.Println("worker<=", time.Now())
}

普通示例3

func Test_CancelTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(5))
	defer cancel()
	fmt.Println("main=>", time.Now())
	go worker()
	for range ctx.Done() {
		fmt.Println("main receive notice", time.Now())
		break
	}
	fmt.Println("main<=", time.Now()) // worker() sleep 10秒,但是依旧5s后退出
}

func worker() {
	fmt.Println("worker=>", time.Now())
	time.Sleep(time.Second * time.Duration(10))
	fmt.Println("worker<=", time.Now())
}

普通示例4

func Test_CancelTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(5))
	defer cancel()
	fmt.Println("main=>", time.Now())
	go worker()
	for range ctx.Done() {
		fmt.Println("main receive notice", time.Now())
		break
	}
	fmt.Println("main<=", time.Now()) // worker() sleep 2秒,但是依旧5s后退出
}

func worker() {
	fmt.Println("worker=>", time.Now())
	time.Sleep(time.Second * time.Duration(2))
	fmt.Println("worker<=", time.Now()) // worker() sleep 2秒,但是依旧5s后退出
}

奇怪示例1

Test_CancelTime函数5秒退出

func Test_CancelTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(5))
	defer cancel()
	fmt.Println("main=>", time.Now())
	go worker(ctx, 100)
	for range ctx.Done() {
		fmt.Println("main receive notice", time.Now())
		break
	}
	fmt.Println("main<=", time.Now())
}

func worker(ctx context.Context, n int) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(10))
	defer cancel()
	fmt.Println("worker=>", time.Now())
	go workInner(ctx, n)
	for range ctx.Done() {
		fmt.Println("worker receive notice", time.Now())
		break
	}
	fmt.Println("worker<=", time.Now())
}

func workInner(ctx context.Context, n int) {
	group, newCtx := errgroup.WithContext(ctx)
	group.SetLimit(3)
	for i := 0; i < n; i++ {
		curIndex := i
		group.Go(func() (err error) {
			defer func() {
				if e := recover(); e != nil {
					logs.CtxError(newCtx, "[workerInner] panic recovered: %v", string(debug.Stack()))
				}
			}()

			fmt.Printf("workerInner %v=>%v\n", curIndex, time.Now())
			time.Sleep(time.Second * time.Duration(2))
			fmt.Printf("workerInner %v<=%v\n", curIndex, time.Now())
			return nil
		})
	}

	if err := group.Wait(); err != nil {
		logs.CtxError(newCtx, "[workerInner] group failed, err=%v", err)
		return
	}
}

奇怪示例2

Test_CancelTime函数依旧是5秒退出

func Test_CancelTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(5))
	defer cancel()
	fmt.Println("main=>", time.Now())
	go worker(ctx, 100)
	for range ctx.Done() {
		fmt.Println("main receive notice", time.Now())
		break
	}
	fmt.Println("main<=", time.Now())
}

func worker(ctx context.Context, n int) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(10))
	defer cancel()
	fmt.Println("worker=>", time.Now())
	go workInner(ctx, n)
	for range ctx.Done() {
		fmt.Println("worker receive notice", time.Now())
		break
	}
	fmt.Println("worker<=", time.Now())
}

func workInner(ctx context.Context, n int) {
	group, newCtx := errgroup.WithContext(ctx)
	group.SetLimit(3)
	for i := 0; i < n; i++ {
		curIndex := i
		group.Go(func() (err error) {
			defer func() {
				if e := recover(); e != nil {
					logs.CtxError(newCtx, "[workerInner] panic recovered: %v", string(debug.Stack()))
				}
			}()

			fmt.Printf("workerInner %v=>%v\n", curIndex, time.Now())
			time.Sleep(time.Second * time.Duration(2))
			fmt.Printf("workerInner %v<=%v\n", curIndex, time.Now())
			return nil
		})
	}

	if err := group.Wait(); err != nil {
		logs.CtxError(newCtx, "[workerInner] group failed, err=%v", err)
		return
	}
}

奇怪示例3

func Test_CancelTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(10))
	defer cancel()
	fmt.Println("main=>", time.Now())
	go worker(ctx, 100)
	for range ctx.Done() {
		fmt.Println("main receive notice", time.Now())
		break
	}
	fmt.Println("main<=", time.Now())
}

func worker(ctx context.Context, n int) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(4))
	defer cancel()
	fmt.Println("worker=>", time.Now())
	go workInner(ctx, n)
	for range ctx.Done() {
		fmt.Println("worker receive notice", time.Now())
		break
	}
	fmt.Println("worker<=", time.Now())
}

func workInner(ctx context.Context, n int) {
	group, newCtx := errgroup.WithContext(ctx)
	group.SetLimit(3)
	for i := 0; i < n; i++ {
		curIndex := i
		group.Go(func() (err error) {
			defer func() {
				if e := recover(); e != nil {
					logs.CtxError(newCtx, "[workerInner] panic recovered: %v", string(debug.Stack()))
				}
			}()

			fmt.Printf("workerInner %v=>%v\n", curIndex, time.Now())
			time.Sleep(time.Second * time.Duration(2))
			fmt.Printf("workerInner %v<=%v\n", curIndex, time.Now())
			return nil
		})
	}

	if err := group.Wait(); err != nil {
		logs.CtxError(newCtx, "[workerInner] group failed, err=%v", err)
		return
	}
}

返回值

main=> 2023-07-05 20:48:34.792095 +0800 CST m=+2.173696454
worker=> 2023-07-05 20:48:34.792172 +0800 CST m=+2.173773493
workerInner 2=>2023-07-05 20:48:34.792234 +0800 CST m=+2.173835427
workerInner 1=>2023-07-05 20:48:34.792265 +0800 CST m=+2.173866728
workerInner 0=>2023-07-05 20:48:34.792269 +0800 CST m=+2.173870488
workerInner 1<=2023-07-05 20:48:36.792364 +0800 CST m=+4.173964493
workerInner 0<=2023-07-05 20:48:36.792383 +0800 CST m=+4.174001401
workerInner 3=>2023-07-05 20:48:36.792411 +0800 CST m=+4.174011412
workerInner 2<=2023-07-05 20:48:36.792446 +0800 CST m=+4.174046582
workerInner 4=>2023-07-05 20:48:36.792444 +0800 CST m=+4.174044861
workerInner 5=>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值