> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/10.0/10.7.md).

# 10.7 关于context包

`context` 包的主要用途是定义 `Context` 类型和支持 **取消**！是的，您没听错；`context` 包的主要用途是支持取消操作，因为有时为了某种原因，您想要废弃正在做的操作。然而，能提供一些关于您取消决定的额外信息是非常有帮助的。`context` 包就能让您做到这点！

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

> `context` *包以前是作为外部 Go 包存在，但在 Go 1.7版本，它第一次作为标准 Go 包出现。因此，如果您有个较老的 Go 版本，再没先下载* `context` *包前，您不能跟着这节学习。*

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

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

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

```go
package main

import (
    "context"
    "fmt"
    "os"
    "strconv"
    "time"
)
```

`simpleContext.go` 的第二部分如下：

```go
func f1(t int) {
    c1 := context.Background()
    c1, cancel := context.WithCancel(c1)
    defer cancel()

    go func(){
        time.Sleep(4 * time.Second)
        cancel()
    }()
```

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

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

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

```go
    select {
        case <- c1.Done():
            fmt.Println("f1():", c1.Err())
            return
        case r := <-time.After(time.Duration(t) * time.Second):
            fmt.Println("f1():", r)
    }
    return
}
```

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

`simpleContext.go` 的第四部分如下：

```go
func f2(t int) {
    c2 := context.Background()
    c2, cancel := context.WithTimeout(c2, time.Duration(t)*time.Second)
    defer cancel()

    go func(){
        time.Sleep(4 * time.Second)
        cancel()
    }()

    select {
        case <-c2.Done():
            fmt.Println("f2():", c2.Err())
            return
        case r := <-time.After(time.Duration(t)*time.Second):
            fmt.Println("f2():", r)
    }
    return
}
```

这部分展示了 `context.WithTimeout()` 函数的使用，它需要两个参数：`Context` 参数和 `time.Duration` 参数。当超时到达时，`cancel()` 函数自动调用。

`simpleContext.go` 的第五部分如下：

```go
func f3(t int) {
    c3 := context.Background()
    deadline := time.Now().Add(time.Duration(2*t) * time.Second)
    c3, cancel := context.WithDeadline(c3, deadline)
    defer cancel()

    go func() {
        time.Sleep(4 * time.Second)
        cancel()
    }()

    select {
        case <-c3.Done():
            fmt.Println("f3():", c3.Err())
            return
        case r := <-time.After(time.Duration(t) * time.Second):
            fmt.Println("f3():", r)
    }
    return
}
```

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

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

```go
func main() {
    if len(os.Args) != 2 {
        fmt.Println("Need a delay!")
        return
    }

    delay, err := strconv.Atoi(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("Delay:", delay)

    f1(delay)
    f2(delay)
    f3(delay)
}
```

执行 `simpleContext.go` 产生如下输出：

```
$go run simpleContext.go 4
Delay: 4
f1(): 2018-02-13 23:30:00.271587 +0200 EET m=+4.003435078
f2(): 2018-02-13 23:30:00.272678 +0200 EET m=+8.004706996
f3(): 2018-02-13 23:30:00.273738 +0200 EET m=+12.005937567
$go run simpleContext.go 10
Delay: 10
f1(): context canceled
f2(): context canceled
f3(): context canceled
```

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

这与使用 `context` 包一样简单，因为介绍的代码没有对 `Context` 接口做任何重要的工作。不过，下节的 Go 代码将介绍一个更真实的例子。
