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,在一定时间后关闭通道,达到一定时间后通过管道通信,来关闭函数