可以自己运行以下代码, 并尝试理解:package main import ( "context" "fmt" "sync" ) func worker(ctx context.Context, wg *sync.WaitGroup) { defer wg.Done() value := ctx.Value("site") fmt.Println(value) } func main() { ctx := context.WithValue(context.Background(), "site", "segmentfault") var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go worker(ctx, &wg) } wg.Wait() }阅读 context 的源码可以得出以下结论:context 包中的 context.WithValue 能从父上下文中创建子上下文, 传值的子上下文使用 context.valueCtx 类型func WithValue(parent Context, key, val interface{}) Context { if parent == nil { panic("cannot create context from nil parent") } if key == nil { panic("nil key") } if !reflectlite.TypeOf(key).Comparable() { panic("key is not comparable") } return &valueCtx{parent, key, val} } 2.context.valueCtx 结构体会将除 Value 外的 Err, Deadline 等方法代理到父上下文中, 它只会响应 context.valueCtx.Value 方法; 当前context查找不到key时,会向父节点查找,如果查询不到则最终返回interface{}。也就是说,可以通过子context查询到父的value值。可以发现写数据的时候必须创建新的子 context, 示例程序中是在主 goroutine 中进行的, 而读数据是在 worker goroutine 中进行的,若读不到则在父 context 中读数据; worker 中传入的 ctx 需要先创建, 可以认为读和写是有前后关系的,且存在多个 goroutine 并发读, 不存在goroutine 并发读写的情况, 所以 context 是并发安全的;
可以自己运行以下代码, 并尝试理解:
阅读 context 的源码可以得出以下结论:
2.context.valueCtx 结构体会将除 Value 外的 Err, Deadline 等方法代理到父上下文中, 它只会响应 context.valueCtx.Value 方法; 当前context查找不到key时,会向父节点查找,如果查询不到则最终返回interface{}。也就是说,可以通过子context查询到父的value值。
可以发现写数据的时候必须创建新的子 context, 示例程序中是在主 goroutine 中进行的, 而读数据是在 worker goroutine 中进行的,若读不到则在父 context 中读数据; worker 中传入的 ctx 需要先创建, 可以认为读和写是有前后关系的,且存在多个 goroutine 并发读, 不存在goroutine 并发读写的情况, 所以 context 是并发安全的;