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 的第一部分代码如下:
package main
import (
"context"
"fmt"
"os"
"strconv"
"time"
)simpleContext.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() 函数的其余部分:
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 的第四部分如下:
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 的第五部分如下:
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 的最后一段代码如下:
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 代码将介绍一个更真实的例子。
Last updated
Was this helpful?