context使用介绍:
对于context上下文的使用,有精力多朋友最好去阅读一下源码,源码的注释写的是真给力。这里列出常用的context用法。
Context接口
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
1. Deadline会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。
2.Done方法返回一个信道(channel),当Context被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。
3. 当Done信道关闭后,Err方法表明Context被撤的原因。
4. Value可以让Goroutine共享一些数据,当然获得数据是协程安全的。但使用这些数据的时候要注意同步,比如返回了一个map,而这个map的读写则要加锁。
goroutine之间会存在层层调用的关系,有点类似于树的结构,一层一层的,父节点子节点。
context.background
类似于树的根节点,它不能被取消、没有值、也没有过期时间。它常常作为处理Request的顶层context存在。
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}
context.todo
如果你不确定你要用什么Context的时候传一个context.TODO;
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
return todo
}
context.WithValue
-
传全局要用的东西,公用的参数就可以用上下文来使用。ctx 是go的一个规范。
-
定位一个请求在哪个子系统出了问题?
traceID,在日志中对此ID做一个聚合分析
func process(ctx context.Context) {
//传递全局公共参数
ret, ok := ctx.Value("trace_id").(string)
if !ok {
ret = ""
}
fmt.Printf("ret:%s\n", ret)
}
func main() {
ret := context.WithValue(context.Background(), "trace_id", "sdfadfaf")
process(ret)
}
context.WithTimeout
- 控制goroutine超时
- 传入的是从现在开始Context剩余的生命时长
- 层层Goroutine根据判断cxt.Done()来结束
package main
import (
"context"
"fmt"
"time"
)
func main() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
}
context.WithDeadline
- 作用根context.timeout差不多
- 过期时间由deadline和parent的过期时间共同决定
- 层层Goroutine根据判断cxt.Done()来结束
context.WithCancel
- 主动撤销Context的方法
package main
import (
"context"
"fmt"
"time"
)
func test(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
case <-ctx.Done()://触发done,然后退出
fmt.Println("exited")
return //结束这个goroutine
case dst <- n:
n++
}
}
}()
return dst
}
func test2() {
//启动后台goroutine时,如何让goroutine退出?
//使用context.WithCancel通知一个线程去结束,就是作为一个信号去抢占select对执行
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
intChan := test(ctx)
for n := range intChan {
fmt.Println(n)
if n == 5 {
break
}
}
}
func main() {
test2()
//防止主线程结束而导致goroutine结束
time.Sleep(3 * time.Second)
}
网友评论