golang源码阅读-context.WithTimeout

context.WithTimeout 可用于一定时间关闭函数或调用,如调用超时,主动关闭调用

1.context.WithTimeout简单使用

func main() {
	ctx, _ := context.WithTimeout(context.Background(), 4*time.Second)
	printNum2(ctx)
}
func printNum2(ctx context.Context) {

	//要打印的数字
	n := 0
	for {
		select {
		case <-ctx.Done():
			fmt.Println("结束")
			return
		default:
			fmt.Println("数字: ", n)
			n++
		}
		time.Sleep(time.Second)
	}
}

2. WithTimeout实现

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	//调用WithDeadline, 当前时间+timeout,时间到了 done管道收到信息,函数自行关闭
	return WithDeadline(parent, time.Now().Add(timeout))
}

3.WithDeadline实现

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
		//时间过了 cancelCtx
		return WithCancel(parent)
	}
	//实例timeCtx
	c := &timeCtx{
		//cancel的上下文
		cancelCtx: newCancelCtx(parent),
		deadline:  d,
	}

	//cancelCtx也调用了这个函数
	propagateCancel(parent, c)
	//时间差
	dur := time.Until(d)
	if dur <= 0 {
		c.cancel(true, DeadlineExceeded)
		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)
	}
}

4.timeCtx 的cancel实现

func (c *timeCtx) cancel(removeFromParent bool, err error) {
	//调用cancelCtx的cancel进行关闭
	c.cancelCtx.cancel(false, err)
	if removeFromParent {
		removeChild(c.cancelCtx.Context, c)
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	//关闭
	if c.timer != nil {
		c.timer.Stop()
		c.timer = nil
	}

}

总结: 在调用WithTimeout时,内部调用的是WithDeadline,实例化timeCtx,而它的父类是cancelCtx. 使用time.AfterFunc,在一定时间后关闭通道,达到一定时间后通过管道通信,来关闭函数