Golang context.WithValue:如何添加几个键值对 [英] Golang context.WithValue: how to add several key-value pairs

查看:4338
本文介绍了Golang context.WithValue:如何添加几个键值对的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Go的 context 包可以将请求特定的数据传递给请求处理函数堆栈

< pre $ func WithValue(父上下文,键,val接口{})上下文

这将创建一个新的 Context ,它是父级副本并包含可以用键访问的值val。



如果我想在 Context 中存储几个键值对,我该如何继续?我应该多次呼叫 WithValue(),每次将从我上次调用中接收到的 Context 传递给 WithValue()?这看起来很麻烦。

或者我应该使用一个结构并将所有的数据放在那里,s.t.我需要只传递一个值(这是结构),从中可以访问所有其他值?



或者是否有一种方法可以将几个键值对 WithValue()

解决方案

您正在寻找的答案取决于您希望如何使用存储在上下文中的值。

context.Context 是一个不可变的对象,只能使用键值对扩展它通过复制它并将新的键值添加到副本中(这是在引擎盖下完成的,由 context 包)。

您是否希望其他处理程序能够通过以下方式访问所有值:以透明的方式键入?然后将所有内容添加到循环中,始终使用最后一个操作的上下文。



这里需要注意的一点是 context.Context 不会在引擎盖下使用 map 来存储键值对,这可能听起来令人惊讶,但如果您想到它肯定是不可变和安全的并发使用。



使用 map



例如,如果您有很多键值对,并且需要通过键 fast 查找值,则分别添加每个值将导致<$ c $其中 Value()方法的c> Context 会很慢。在这种情况下,如果您将所有键值对添加为单个 map 值(可通过 Context.Value(),其中的每个值都可以通过 O(1)时间中的关联键查询。注意,这对于并发使用来说并不安全,因为映射可能会从并发goroutine中修改。



使用 struct



如果您使用 struct 所有你想添加的键值对,这也可能是一个可行的选择。用 Context.Value()访问这个结构会返回一个结构体的副本,所以它对于并发使用是安全的(每个goroutine只能得到一个不同的副本) ,但是如果你有许多键值对,每次有人需要单个字段时,这会导致不必要的大型结构副本。



使用混合解决方案解决方案

混合解决方案可以将所有键值对一个 map ,并为这个地图创建一个封装结构,隐藏 map (未导出的字段),并只提供一个获取地图中存储的值。只将这个包装添加到上下文中,你为多个goroutines( map 是未导出的)保留安全的并发访问权限,但是没有大数据需要被复制 map 值是没有键值数据的小描述符),并且仍然是 fast 最终你会索引一张地图)。

这是它的样子:

 类型值结构{
m map [string] string
}

func(v值)获取(键字符串)字符串{
return vm [key]
}

使用它:

  v:=值{map [string] string {
1:one,
2:two,
$}

c:= context.Background()
c2:= context.WithValue(c,myvalues,v)

fmt.Println (c2.Value(myvalues)。(Values).Get(2))

输出(在去游乐场试试):

  two 

并不重要(或者你有相对较少的键值对),我会分开添加每一个。


With Go's context package it is possible to pass request-specific data to the stack of request handling functions using

func WithValue(parent Context, key, val interface{}) Context

This creates a new Context which is a copy of parent and contains the value val which can be accessed with key.

How do I proceed if I want to store several key-value pairs in a Context? Shall I call WithValue() several times, each time passing the Context received from my last call to WithValue()? This appears cumbersome.
Or shall I use a struct and put all my data there, s.t. I need to pass only one value (which is the struct), from which all others can be accessed?

Or is there a way of passing several key-value pairs to WithValue()?

解决方案

You pretty much listed your options. The answer you're seeking for depends on how you want to use the values stored in the context.

context.Context is an immutable object, "extending" it with a key-value pair is only possible by making a copy of it and adding the new key-value to the copy (which is done under the hood, by the context package).

Do you want further handlers to be able to access all the values by key in a transparent way? Then add all in a loop, using always the context of the last operation.

One thing to note here is that the context.Context does not use a map under the hood to store the key-value pairs, which might sound surprising at first, but not if you think about it must be immutable and safe for concurrent use.

Using a map

So for example if you have a lot of key-value pairs and need to lookup values by keys fast, adding each separately will result in a Context whose Value() method will be slow. In this case it's better if you add all your key-value pairs as a single map value, which can be accessed via Context.Value(), and each value in it can be queried by the associated key in O(1) time. Know that this will not be safe for concurrent use though, as a map may be modified from concurrent goroutines.

Using a struct

If you'd use a big struct value having fields for all the key-value pairs you want to add, that may also be a viable option. Accessing this struct with Context.Value() would return you a copy of the struct, so it'd be safe for concurrent use (each goroutine could only get a different copy), but if you have many key-value pairs, this would result in unnecessary copy of a big struct each time someone needs a single field from it.

Using a hybrid solution

A hybrid solution could be to put all your key-value pairs in a map, and create a wrapper struct for this map, hiding the map (unexported field), and provide only a getter for the values stored in the map. Adding only this wrapper to the context, you keep the safe concurrent access for multiple goroutines (map is unexported), yet no big data needs to be copied (map values are small descriptors without the key-value data), and still it will be fast (as ultimately you'll index a map).

This is how it could look like:

type Values struct {
    m map[string]string
}

func (v Values) Get(key string) string {
    return v.m[key]
}

Using it:

v := Values{map[string]string{
    "1": "one",
    "2": "two",
}}

c := context.Background()
c2 := context.WithValue(c, "myvalues", v)

fmt.Println(c2.Value("myvalues").(Values).Get("2"))

Output (try it on the Go Playground):

two

If performance is not critical (or you have relatively few key-value pairs), I'd go with adding each separately.

这篇关于Golang context.WithValue:如何添加几个键值对的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆