1.context简介
在go的http包的server中,每一个对应的请求都有一个goroutine负责处理,处理函数通常会启动额外的goroutine去处理,如果一个请求被取消或者超时,用来处理该请求的goroutine应该及时退出,这样就不会有大量的goroutine去占用资源。
Context类型专用来简化对于处理单个请求的多个goroutine之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个API调用。因此对服务器的请求应该去创建上下文,对服务器的传输调用也应该接收上下文,它们的函数调用必须传上下文,或可以使用WithCancel、WithDeadline、WithTimeout或WithValue创建的派生上下文。当一个上下文取消时 它派生的所有上下文也会取消。
context.Context是一个接口,接口定义了四个实现的方法:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
- Deadline:返回的第一个值是 截止时间,到了这个时间点,Context 会自动触发 Cancel 动作。返回的第二个值是 一个布尔值,true 表示设置了截止时间,false 表示没有设置截止时间,如果没有设置截止时间,就要手动调用 cancel 函数取消 Context。
- Done:返回一个只读的通道(只有在被cancel后才会返回),类型为 struct{}。当这个通道可读时,意味着parent context已经发起了取消请求,根据这个信号,开发者就可以做一些清理动作,退出goroutine。 多次调用Done方法会返回同一个Channel。
- Err:返回 context 被 cancel 的原因。它只会在Done返回的Channel关闭时才会返回非空的值,如果当前Context被取消就会返回Canceled,如果当前Context超时就会返回DeadlineExceeded。
- Value:返回被绑定到 Context 的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。对于同一个上下文来 说,多次调用Value 并传入相同的Key会返回相同的结果,该方法仅用于传递跨API和进程间请求域的数据。
2.context解决的问题
当有多个goroutine在运行的时候,主goroutine如果提前结束,会导致其余的goroutine没有执行完毕,我们一般来用互斥锁+全局变量或者channel来解决。如下:
var wg sync.WaitGroup
var exit bool
// 全局变 方式存在的
// 1. 使用全局变量在跨包调用时不容易统一
// 2. 如果worker中再启动goroutine 就不太好控制了。
func worker() {
for {
fmt.Println("worker")
time.Sleep(time.Second)
if exit {
break
}
}
wg.Done()
}
func main() {
wg.Add(1)
go worker()
time.Sleep(time.Second * 3)
exit &