go context

WithTimeout

WithTimeout会在一个goroutine中开启一个定时器,到期之后,自动取消对应的ctx,同时,所有从该ctx派生的子ctx都会被取消。

package ctxtest

import (
	"context"
	"testing"
	"time"
)

// go test -run TestCtx -v deprecated_loader/ctxtest/*.go
func TestCtx(t *testing.T) {
	ctx := context.Background()

	// WithTimeout will start a timer, after which ctx's Done is closed
	ctx, cancel := context.WithTimeout(ctx, 1000*time.Millisecond)
	defer cancel()

	time.Sleep(500 * time.Millisecond)

	err := ctx.Err()
	if err != nil {
		t.Fatalf("expect ctx err to be nil, actaul:%v", err)
	}
	time.Sleep(510 * time.Millisecond)

	err2 := ctx.Err()
	if err2 == nil {
		t.Fatalf("expect ctx err to be not nil after timeout")
	}
	t.Logf("err2:%v", err2)
}

WithTimeout(0)

你可能会好奇,当WithTimeouttimeout参数是0,或者负数时,会有什么表现。实际上,表现就是,在返回的时候,该context已经超时取消了。因为,实际上timeout的作用是用于确定deadline,即deadline = now + timeout,所以timeout <= 0时,只会使得deadline已经到达,直接被cancel掉(参考context源码,下面第21行)。

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
		// The current deadline is already sooner than the new one.
		return WithCancel(parent)
	}
	c := &timerCtx{
		cancelCtx: newCancelCtx(parent),
		deadline:  d,
	}
	propagateCancel(parent, c)

    // ******************** time.Until(d) = d - now **************
    // 当 d - now < 0, 即d < now时,此处直接调用cancel,不需要计时器的参与
	dur := time.Until(d)
	if dur <= 0 {
		c.cancel(true, DeadlineExceeded) // deadline has already passed
		return c, func() { c.cancel(false, Canceled) }
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err == nil {
		c.timer = time.AfterFunc(dur, func() {
			c.cancel(true, DeadlineExceeded)
		})
	}
	return c, func() { c.cancel(true, Canceled) }
}

测试代码:

package util

import (
	"context"
	"testing"
	"time"
)

// go test -run TestZeroTimeout -v util/*.go
func TestZeroTimeout(t *testing.T) {
	ctx := context.Background()
	cancelCtx, cancel := context.WithTimeout(ctx, time.Duration(0)*time.Millisecond)
	defer cancel()

	select {
	case <-cancelCtx.Done():
		t.Logf("done") // prints: done
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值