2
头图

Original link: Interviewer: Is it thread-safe for Context to carry data?

foreword

Hello, everyone, my name is asong . I saw an interesting eight-part article in a group recently. The question is: Is it thread-safe to use context carried by value ? This question is actually to examine the interviewee's understanding of the implementation principle of context . If you do not know the implementation principle of context , it is easy to answer this question incorrectly, so in this article, we will use this question to re-understand the implementation of context carrying value principle.

context carrying value thread safe?

Let’s talk about the answer first, context itself is thread-safe, so context carrying value is also thread-safe, write a simple example to verify:

func main()  {
    ctx := context.WithValue(context.Background(), "asong", "test01")
    go func() {
        for {
            _ = context.WithValue(ctx, "asong", "test02")
        }
    }()
    go func() {
        for {
            _ = context.WithValue(ctx, "asong", "test03")
        }
    }()
    go func() {
        for {
            fmt.Println(ctx.Value("asong"))
        }
    }()
    go func() {
        for {
            fmt.Println(ctx.Value("asong"))
        }
    }()
    time.Sleep(10 * time.Second)
}

The program runs normally without any problems.
However context has no type restrictions on the data carried, so any data type is carried by context . When the carried data type is a pointer type, it is not thread-safe. Let's take an example:

func main()  {
    m := make(map[string]string)
    m ["asong"] = "Golang梦工厂"
    ctx := context.WithValue(context.Background(), "asong", m)
    go func() {
        for {
            m1 := ctx.Value("asong")
            mm := m1.(map[string]string)
            mm["asong"] = "123213"
        }
    }()
    go func() {
        for {
            m1 := ctx.Value("asong")
            mm := m1.(map[string]string)
            mm["asong"] = "123213"
        }
    }()
    time.Sleep(10 * time.Second)
}

operation result:

fatal error: concurrent map writes

goroutine 18 [running]:
runtime.throw({0x1072af2, 0x0})
......

Why is thread safe?

context package provides two ways to create the root context :

  • context.Backgroud()
  • context.TODO()

It also provides four functions derived from the parent Context , of which the WithValue function is used to derive context and carry data. Each time the WithValue function is called, a new child context will be derived based on the current context . WithValue is mainly called 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}
}

The structure of valueCtx is as follows:

type valueCtx struct {
 Context
 key, val interface{}
}

valueCtx inherits the parent Context , which is an inheritance implementation using anonymous interfaces, and key,val used to store the carried key-value pairs.

Through the above code analysis, it can be seen that adding key-value pairs is not directly added to the original context structure, but context as the parent node, recreates a new valueCtx child node, and adds the key-value pair to the child node, This forms a context chain.

The process of obtaining the key value is also called up layer by layer until the final root node. If key is found in the middle, it will return, otherwise it will find the final emptyCtx return nil .

Draw a diagram to represent:

image-20220207214507921

Summary: The key-value pair added by context is a chain, and new context will be continuously derived, so context itself is immutable, so it is thread-safe, but if the data we carry is a pointer type, there are still threads that cannot be security risk.

Summarize

This article mainly wants to take everyone to review the implementation principle of context . During the interview, interviewers like to ask questions vaguely, so this requires us to have very solid basic skills. If we are not careful, we will fall into the trap of the interviewer. Be careful everywhere. ~

Well, this article ends here, I am asong , see you in the next issue.

Welcome to the public account: [Golang Dream Factory]


asong
605 声望906 粉丝