1 个回答

可以自己运行以下代码, 并尝试理解:

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 的源码可以得出以下结论:

  1. 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 是并发安全的;

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏