ale*_*lex 24 concurrency go goroutine
使用Go的context包,可以使用特定于请求的数据传递给请求处理函数堆栈
func WithValue(parent Context, key, val interface{}) Context
Run Code Online (Sandbox Code Playgroud)
这将创建一个new Context,它是parent的副本,并包含可以使用key访问的值val.
如果我想在一个Context?中存储几个键值对,我该如何继续?我应该WithValue()多次拨打电话,每次Context从我上次拨打的电话都收到通话WithValue()?这看起来很麻烦.
或者我应该使用一个结构并将所有数据放在那里,我需要只传递一个值(结构),从中可以访问所有其他值?
或者有没有办法将几个键值对传递给WithValue()?
icz*_*cza 29
你几乎列出了你的选择.您正在寻找的答案取决于您希望如何使用存储在上下文中的值.
context.Context是一个不可变对象,只有通过复制它并将新的键值添加到副本(通过context包在引擎盖下完成),才能用键值对"扩展"它.
您是否希望其他处理程序能够以透明方式按键访问所有值?然后在循环中添加all,始终使用上一个操作的上下文.
这里需要注意的一点是,context.Context不会使用map底层来存储键值对,这一开始可能听起来令人惊讶,但如果您认为它必须是不可变的并且对于并发使用是安全的则不会.
用一个 map
因此,例如,如果您有很多键值对并且需要快速按键查找值,则单独添加每个键将导致Context其Value()方法速度很慢.在这种情况下,最好将所有键值对添加为单个map值,可以通过该值访问Context.Value(),并且相关键可以及时查询其中的每个值O(1).知道这对于并发使用是不安全的,因为可以从并发goroutine修改映射.
用一个 struct
如果您使用包含struct要添加的所有键值对的字段的大值,那么这也可能是一个可行的选项.访问此结构Context.Value()将返回结构的副本,因此它对于并发使用是安全的(每个goroutine只能获得不同的副本),但是如果你有许多键值对,这将导致不必要的副本每当有人需要一个字段时,一个大结构.
使用混合解决方案
一个混合的解决办法是把在所有键值对map,并创建一个包装结构为这个地图,隐藏map(不导出字段),并提供了对存储在地图的值的吸气剂.仅将此包装器添加到上下文中,您可以保持多个goroutine 的安全并发访问(map未导出),但不需要复制大数据(map值是没有键值数据的小描述符),并且它仍然会很快(最终你会为地图编制索引).
这是它的样子:
type Values struct {
m map[string]string
}
func (v Values) Get(key string) string {
return v.m[key]
}
Run Code Online (Sandbox Code Playgroud)
使用它:
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"))
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
two
Run Code Online (Sandbox Code Playgroud)
如果性能不是很关键(或者您的键值对相对较少),我会单独添加每个键值对.
小智 17
context要创建具有多个键值的golang,您可以WithValue多次调用方法。context.WithValue(basecontext, key, value)
ctx := context.WithValue(context.Background(), "1", "one") // base context
ctx = context.WithValue(ctx, "2", "two") //derived context
fmt.Println(ctx.Value("1"))
fmt.Println(ctx.Value("2"))
Run Code Online (Sandbox Code Playgroud)
在操场上观看它的实际表现
Sam*_*ted 14
是的,你是对的,你每次都需要打电话WithValue()给结果.要理解为什么它以这种方式工作,值得思考一下背景理论背后的理论.
上下文实际上是上下文树中的节点(因此各种上下文构造函数采用"父"上下文).当您从上下文请求值时,您实际上是在从相关上下文开始,在搜索树时请求找到与您的键匹配的第一个值.这意味着如果您的树有多个分支,或者您从分支中的较高点开始,则可以找到不同的值.这是语境力量的一部分.另一方面,取消信号将树向下传播到已取消的子元素,因此您可以取消单个分支,或取消整个树.
例如,这是一个上下文树,其中包含您可能在上下文中存储的各种内容:
黑色边缘表示数据查找,灰色边缘表示取消信号.请注意,它们以相反的方向传播.
如果您使用地图或其他结构来存储密钥,则宁可打破上下文的范围.您将无法仅取消请求的一部分,或者例如.根据您所处的请求的哪个部分更改记录事项的位置等.
TL; DR - 是的,多次调用WithValue.
正如“icza”所说,您可以将值分组在一个结构中:
type vars struct {
lock sync.Mutex
db *sql.DB
}
Run Code Online (Sandbox Code Playgroud)
然后你可以在上下文中添加这个结构:
ctx := context.WithValue(context.Background(), "values", vars{lock: mylock, db: mydb})
Run Code Online (Sandbox Code Playgroud)
你可以检索它:
ctxVars, ok := r.Context().Value("values").(vars)
if !ok {
log.Println(err)
return err
}
db := ctxVars.db
lock := ctxVars.lock
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
14475 次 |
| 最近记录: |