在golang的上下文中关于withcancel和withtimeout的不同

时间:2019-06-23 06:48:50

标签: go

我是Golang的新手,在学习golang的上下文部分时,我对WithCancelWithTimeout感到困惑。

显示代码。

package main

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

func someHandler() {
    //ctx, cancel := context.WithCancel(context.Background())
    ctx, cancel := context.WithTimeout(context.Background(),  2*time.Second)
    go doSth(ctx)

    time.Sleep(3 * time.Second)
    cancel()
}

func doSth(ctx context.Context) {
    var i = 1
    for {
        time.Sleep(1 * time.Second)
        select {
        case <-ctx.Done():
            fmt.Println("done")
            return
        default:
            fmt.Printf("work %d seconds: \n", i)
        }
        i++
    }
}

func main() {
    fmt.Println("start...")
    someHandler()
    fmt.Println("end.")
}

结果:

// when use WithCancel
//
start...
work 1 seconds: 
work 2 seconds: 
end.

// when use WithTimeout
start...
work 1 seconds:
done
end.

我的问题是:为什么当我使用done却打印withCancel时却不打印'withTimeout'?

2 个答案:

答案 0 :(得分:1)

来自Understanding the context package in golang

Parikshit Agnihotry”  提到:

  

context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)

     

此函数创建一个新的上下文,该上下文派生自传入的父上下文。
  父级可以是背景上下文,也可以是传递给函数的上下文。

     

这将返回派生上下文和cancel函数。
  只有创建此函数的函数才应调用cancel函数来取消此上下文。
  您可以根据需要传递取消功能,但是强烈建议不要这样做。这可能导致取消的调用者无法意识到取消上下文的下游影响可能是什么。从中可能衍生出其他上下文,这些上下文可能导致程序以意外的方式运行。简而言之,永远不要绕过取消功能。

ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2 * time.Second))
     

context.WithDeadline(parent Context, d time.Time) (ctx Context, cancel CancelFunc)

     

此函数从其父级返回派生上下文,当超过期限或调用cancel函数时,该上下文将被取消。

     

例如,您可以创建一个上下文,该上下文将在将来的某个时间自动取消,并将其传递给子函数。
  由于截止期限而取消上下文时,会通知所有获得上下文的功能以停止工作并返回。

ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2 * time.Second))
  

context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)

     

此功能类似于context.WithDeadline
  区别在于它以持续时间作为输入而不是时间对象作为输入
  此函数返回派生的上下文,如果调用cancel函数或超过超时时间,则该上下文将被取消。

ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2 * time.Second))

  

为什么与Cancel一起使用时为什么不打印'done'但withTimeout可以

可能是因为Go程序在goroutine有时间确认“完成”部分之前已经退出。

答案 1 :(得分:0)

我们可以在源码中找到cancelContext、timeOutContext和deadlineContext的关系如下:

// cancelCtx declaration

type cancelCtx struct {
    Context

    mu       sync.Mutex            // protects following fields
    done     chan struct{}         // created lazily, closed by first cancel call
    children map[canceler]struct{} // set to nil by the first cancel call
    err      error                 // set to non-nil by the first cancel call
}

// timerCtx declaration
type timerCtx struct {
    cancelCtx
    timer *time.Timer // Under cancelCtx.mu.

    deadline time.Time
}

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

cancelCtx 内嵌在timerCtx中,实现golang中的继承。