【问题标题】:Updating struct via pointer of pointer not working通过指针的指针更新结构不起作用
【发布时间】:2020-10-15 20:57:37
【问题描述】:

好的,对于一些简化的设置,在这个例子中,我们有三个结构 comp、agg 和 cache,看起来有点像这样:

type comp struct {
    id  uint64
    val float64
}

type agg struct {
    id   uint64
    vals []*comp
}

type cache struct {
    compMtx sync.RWMutex
    comps map[uint64]*comp
    aggMtx sync.RWMutex
    aggs  map[uint64]*agg
}

cache 具有以下功能来添加新的comp 值,在更新的情况下似乎不起作用:

func (c *cache) NewComp(cpNew *comp) {
    compMtx.Lock()
    defer compMtx.Unlock()
    cpOld, ok := c.comps[cpNew.id]
    if ok { // update
        addr := &cpOld // this is of type **comp
        *addr = cpNew
    } else { // new value
        c.comps[cpNew.id] = cpNew
    }
}

这种方法背后的思想是,通过改变指针指向的位置,我们可以确保agg.vals 中的comp 指针始终指向给定comp 对象的最新迭代。

至于这种方法背后的原因,遍历整个 agg.vals 数组以查找给定 comp 对象的索引将 a) 由于数组的(相当大)大小而计算量很大,并且b) 要求在搜索期间通过内部sync.Mutex 锁定agg,以阻止不同的线程访问该对象,这两者都是不可取的。此外假设不可能将agg.value 制作成地图以方便更新。

由于上面实现的NewComp不起作用,我的问题是上面的函数是否有任何明显的错误,或者我在这里的想法是否犯了一些基本错误?


由于这可能会有所帮助,这里还有一个示例,其中通过指针的指针进行更新按预期工作:

type wrks struct {
    id   uint64
    val1 *comp
    val2 *comp
}

func compute() *comp {...}

func updateComp(c **comp) {
    *c = compute()
}

func processObj(o *obj) {
    updateComp(&o.val1)
    updateComp(&o.val2)
}

我看不出两者之间的根本区别,但我可能在这一点上盯着这个看太久了。

【问题讨论】:

    标签: dictionary go pointers struct


    【解决方案1】:

    你将指针存储在map中,所以当你从中获取指针时,只需修改指向的值,赋值给指向的值:

    cpOld, ok := c.comps[cpNew.id]
    if ok { // update
        *cpOld = *cpNew
    } else { // new value
        c.comps[cpNew.id] = cpNew
    }
    

    请参阅Go Playground 上的简化示例,该示例显示此方法有效。

    您的原始代码不起作用,因为cpOld 是一个指针,但它是存储在映射中的指针的副本。如果您修改此 cpOld 变量的值,则不会影响映射中存储的值。您不能更改地图中的值,只能在其中重新分配/存储新值。

    你应该记住一点:传递给NewComp()的cpNew指针在更新后将不会“有用”,因为我们没有使用指针,我们只是使用指向的值来更新值地图已经指向的(地图中存储的值)。如果您希望指针继续指向相同的值,您别无选择,只能将该指针存储在映射中,就像它是新的一样:

    cpOld := c.comps[cpNew.id] = cpNew
    

    查看相关问题:How to update map values in Go

    【讨论】:

    • 我明白了,通过*cpOld = *cpNew 更新也不能解决问题。我之前尝试过,可能应该提到这一点。
    • 嗯,谢谢,所以如果指针始终是映射中指针的副本,那么将指针的指针存储在映射中并从**comp 的副本重新分配工作吗?
    • 但它有效,请参阅Go Playground 上的简化示例。
    • 索引地图将为您提供地图中存储的值的副本。您将该副本存储在cpOld 中,但cpOld 不与存储在映射中的值共享内存。无论您对 cpOld 变量做什么,都不会更改存储在地图中的值。
    • 哦,是的,在操场上它确实按预期工作。很奇怪,它在我的情况下不起作用,然后似乎我在其他地方有一个错误,我必须调查......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-11
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多