关于context包

    如果您浏览一下 context 包点源码,就会认识到它的实现是相当的简单——甚至 Context 类型的实现也非常简单,而 context 包是非常重要的。

    Context 类型是一个有四个方法的接口,方法名为 Deadline()Done()Err()Value()。好消息是您不需要实现 Context 接口的所有方法——您只需要使用如 context.WithCancel()context.WithTimeout() 函数修改 Context 变量就行。

    下面是使用 context 包的示例,用 simpleContext.go 文件源码,分六部分来介绍。

    simpleContext.go 的第一部分代码如下:

    1. func f1(t int) {
    2. c1 := context.Background()
    3. c1, cancel := context.WithCancel(c1)
    4. defer cancel()
    5. go func(){
    6. time.Sleep(4 * time.Second)
    7. cancel()
    8. }()

    f1() 函数只需要一个时延参数,因为其他的都定义在函数里了。注意 变量的类型是 context.CancelFunc

    您需要调用 context.Background() 函数来初始化一个空 Context 参数。context.WithCancel() 函数使用一个存在的 Context 创建一个子类并执行取消操作。context.WithCancel() 函数也会创建一个 Done 通道,如上面的代码所示当 cancel() 函数被调用时,或者当父 context 的 Done 通道关闭时,它会被关闭。

    simpleContext.go 的第三部分包含 f1() 函数的其余部分:

    这里您看到了 Context 变量的 Done() 函数的使用。当这个函数被调用时,您有一个取消操作。Context.Done() 的返回值是一个通道,否则您就不能在 select 语句中使用它了。

    simpleContext.go 的第四部分如下:

    1. func f2(t int) {
    2. c2 := context.Background()
    3. c2, cancel := context.WithTimeout(c2, time.Duration(t)*time.Second)
    4. defer cancel()
    5. time.Sleep(4 * time.Second)
    6. cancel()
    7. }()
    8. select {
    9. case <-c2.Done():
    10. fmt.Println("f2():", c2.Err())
    11. return
    12. case r := <-time.After(time.Duration(t)*time.Second):
    13. }
    14. return
    15. }

    simpleContext.go 的第五部分如下:

    上面的代码说明了 context.WithDeadline() 函数的使用,它需要两个参数:Context 变量和一个表示操作将要截止的时间。当期限到了,cancel() 函数自动调用。

    simpleContext.go 的最后一段代码如下:

    1. func main() {
    2. fmt.Println("Need a delay!")
    3. return
    4. }
    5. delay, err := strconv.Atoi(os.Args[1])
    6. if err != nil {
    7. fmt.Println(err)
    8. return
    9. }
    10. fmt.Println("Delay:", delay)
    11. f1(delay)
    12. f2(delay)
    13. f3(delay)

    执行 simpleContext.go 产生如下输出:

    输出较长的行是 time.After() 函数调用的返回值。它们代表程序正常操作。意味着如果该程序执行超时就会立刻被取消。