Go: Context 理解
Sun ,Jun 25 ,2017在看proxy的时候,看到了context这个package.
source 1: Package context
Incoming requests to a server should create a Context,
and outgoing calls to servers should accept a Context
> * 对于,访问本服务器的Incoming 请求应该建立一个Context.
> * 对于,从本服务器访问其他服务器的Outgoing 请求应该接收Context.
下面的链接中包含了创建一个context的代码。
source 2: Go Concurrency Patterns: Context
WithCancel
WithDeadline
WithTimeout
WithValue
以上函数根据不同的含义,分别返回parent Context的child Context。
package main
import (
"context"
"fmt"
"time"
)
func main() {
d := time.Now().Add(50 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
// Even though ctx will be expired, it is good practice to call its
// cancelation function in any case. Failure to do so may keep the
// context and its parent alive longer than necessary.
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
总结:Context是Go提供的一种机制,可以完成:
场景1:有一个请求,其中包含了若干子请求。当子请求还是与对应的服务器通信时,父请求因为各种各样的原因停止了,
此时,单纯的关闭父请求是没有意义的,Context机制就可以实现关闭父请求的同时,关闭其他的子请求。
场景2:传递 request-scoped 数据。传递动态上下文所需的数据。
示例如下:
package main
import (
"context"
"fmt"
"time"
)
type key int
func main() {
ctx, cancel := context.WithCancel(context.Background())
t := key(1)
ctx_child := context.WithValue(ctx, t, "hello")
go test(ctx_child)
cancel()
time.Sleep(1 * time.Second)
}
func test(ctx context.Context) {
t := key(1)
fmt.Printf("%s \n", ctx.Value(t))
select {
case <-ctx.Done():
fmt.Printf("bye bye in child \n")
}
}
output:
hello
bye bye in child
上面的程序刻意创造了两层结构,验证了parent ctx关闭时,child ctx也关闭,同时也实现了 scope传值。
ctx在使用中,官方推荐的是一层层深入,每一层都需要创建新的ctx,传递给新的go程,同时在本层中关闭。
调用必须是同步的调用。原因是上层的context调用,会关闭下层所有的 context的调用,则下层的各个功能模块还没有完成任务,而中途关闭。
反过来说,当上层出现问题的时候,这个作用刚好可以保证所有的下层都正常关闭。
参考:go-context使用
正确的调用:
1层
生成child_ctx
defer child_cancel()
2层调用(ctx)
起go程跑任务
select {
case: ctx.Done() //捕捉上层的异常,或者ctx本身关闭
case: 任务完成
}
2层结束
1层结束
错误的调用:
1层
2层调用
起Go程去跑任务
2层结束
1层结束
这时候Go程还在跑,结果还没有出来,就被上层强制关闭了,逗。