context包
被称为上下文包,go 1.7加入,用于协程之间的上下文数据的传递、中止核控制超时。
在网络编程中可用于请求的中止,比如服务访问链的中止:a用户注册->b调用用户服务->c调用积分服务
。
其中a调用b,b调用c。如果由于a和b之间因为某些原因被取消或者超时了,那么b和c之间也要取消。
Context接口
源码解读:
type Context interface{
//返回一个超时时间
Deadline()(deadline time.Time, ok bool)
//返回只读channel
//一旦可读,代表父context发起取消操作,通过该方法可以收到此信号
//完成协程退出并返回Err()
Done()<-chan struct{}
//返回context被取消的原因
Err() error
//获取Context上绑定值(根据key),线程安全
Value(key interface{}) interface{}
}
空上下文
在进行上下文控制之前,首先要创建一个顶层context。
go内置包提供了一个方法:
context.Background()
WithTimeout超时自动取消
当执行一个go协程时,超时自动取消协程
package main
import (
"context"
"fmt"
"time"
)
// 模拟一个最小执行时间的阻塞函数
func incr(a int) int {
res := a + 1
time.Sleep(time.Second * 1)
return res
}
// 提供一个阻塞接口
// 计算a+b,注意a,b不能是负数
// 如果计算被中断,则返回-1
func Add(ctx context.Context, a, b int) int {
fmt.Printf("开始")
res := 0
for i := 0; i < a; i++ {
res = incr(res)
select {
case <-ctx.Done(): //父context超时了
return -1
}
}
for i := 0; i < b; i++ {
res = incr(res)
select {
case <-ctx.Done(): //父context超时了
return -1
}
}
return res
}
func main() {
ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
res := Add(ctx, 1, 2)
fmt.Println(res) //-1
}
WithCancel 手动取消方法
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(time.Second * 2)
cancel() //在调用处主动取消
}()
res := Add(ctx, 1, 2)
fmt.Println(res) //-1
}