dongtang5229 2016-11-02 12:28
浏览 2453
已采纳

Golang context.WithValue:如何添加几个键值对

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()?

  • 写回答

3条回答 默认 最新

  • dongzhuonao8429 2016-11-02 12:37
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 单通道放大电路的工作原理
  • ¥30 YOLO检测微调结果p为1
  • ¥20 求快手直播间榜单匿名采集ID用户名简单能学会的
  • ¥15 DS18B20内部ADC模数转换器
  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题