关于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 代码将介绍一个更真实的例子。