Original link: Interviewer: Is it thread-safe for Context to carry data?
foreword
Hello, everyone, my name isasong
. I saw an interesting eight-part article in a group recently. The question is: Is it thread-safe to usecontext
carried byvalue
? This question is actually to examine the interviewee's understanding of the implementation principle ofcontext
. If you do not know the implementation principle ofcontext
, it is easy to answer this question incorrectly, so in this article, we will use this question to re-understand the implementation ofcontext
carryingvalue
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:
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]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。