context
2009 年 6 月 8 日
context:
主要的用处如果用一句话来说,是在于简化在多个go routine传递上下文数据,控制goroutine的生命周期。
应用场景:
1.官方http包使用context传递请求的上下文数据
2.gRpc使用context来终止某个请求产生的routine树
3.关联任务的取消
…….
A Context carries a deadline, a cancellation signal, and other values across
基本操作:
import ( "context" ) 根Context:通过context.Background() 创建 子Context:context.WithCancel(parentContext) 创建 ctx,cancel := context.WithCancel(context.Backgroup()) ctx可以传入到接下来的子任务,cancel可以取消当前Context, 当前Context被取消,基于他的子context都会被取消 接受取消通知 <- ctx.Done()
context.Background(),创建一个 根Context实例 =》 emptyCtx
func Background() Context { return background } background = new(emptyCtx)
type Context interface { //返回一个time.Time,表示当前Context应该结束的时间,ok则表示有结束时间 Deadline() (deadline time.Time, ok bool) //返回一个关闭的channel (Done returns a channel that's closed )当timeout或者调用cancel方法时,将会触发Done Done() <-chan struct{} //context被取消的原因 Err() error //context实现共享数据存储的地方,是协程安全的 Value(key interface{}) interface{} }
context库中,有4个关键方法:
-
WithCancel
返回一个cancel函数,调用这个函数则可以主动停止goroutine。
func main() { root := context.Background() //ctx1, cancel := context.WithDeadline(root, time.Now().Add(6 * time.Second)) ctx1, cancel := context.WithCancel(root) ctx2 := context.WithValue(ctx1, "key2", "value2") go watch(ctx2) time.Sleep(10 * time.Second) fmt.Println("通知监控停止") cancel() time.Sleep(5 * time.Second) } func watch(ctx context.Context) { for { select { case <- ctx.Done(): fmt.Println(ctx.Value("key2"), "监控退出了。") return default: fmt.Println(ctx.Value("key2"), "go rountine 监控中。。。") time.Sleep(2 * time.Second) } } } 输出: value2 go rountine 监控中。。。 value2 go rountine 监控中。。。 value2 go rountine 监控中。。。 value2 go rountine 监控中。。。 value2 go rountine 监控中。。。 通知监控停止 value2 监控退出了。
-
WithValue
WithValue可以设置一个key/value的键值对,可以在下游任何一个嵌套的context中通过key获取value。
func main() { root := context.Background() ctx1 := context.WithValue(root, "key1", "value1") ctx2 := context.WithValue(ctx1, "key2", "value2") fmt.Println(ctx1.Value("key1")) fmt.Println(ctx1.Value("key11")) fmt.Println(ctx2.Value("key2")) } 输出: value1 value2
-
WithTimeout
函数可以设置一个time.Duration,到了这个时间则会cancel这个context。
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) } shutDown := func(interface{}) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if e := httpSrv.Shutdown(ctx); e != nil { log.Errorf("cronsun node pprof, metrics http server shutdown failed, error: %s", e.Error()) } else { log.Errorf("cronsun node pprof, metrics http server shutdown success~~") } }
-
WithDeadline
WithDeadline函数跟WithTimeout很相近,只是WithDeadline设置的是一个时间点。
func main() { d := time.Now().Add(500 * time.Millisecond) //500 ms ctx, cancel := context.WithDeadline(context.Background(), d) defer cancel() c := make(chan bool) //模拟耗时代码 go func() { time.Sleep(400 * time.Millisecond) //修改数值大于 500 c <- true //返回 }() select { case flag := <-c: //从chan 获取值 if flag { fmt.Println("执行任务成功") } else { fmt.Println("执行任务失败") } case <-ctx.Done(): //是ctx的通道先返回的数据 fmt.Println("执行任务超时~") } }
欢迎关注我们的微信公众号,每天学习Go知识