【问题标题】:Is it possible to check whether a field has been set without triggering a race condition?是否可以在不触发竞争条件的情况下检查字段是否已设置?
【发布时间】:2020-02-13 08:53:07
【问题描述】:

我有一个在许多 goroutine 之间共享的结构,如果可能的话,我想减少计算结构上一个字段的次数。可能更容易显示:

type Container struct {
    Items []Item
    mu    sync.RWMutex
}

(container *Container) func loadContainer() {
    if container.Items != nil { // This is detected as a race condition by go
        return 
    }
    container.mu.Lock()
    container.Items = loadItems() // Does some logic that I would like to avoid repeating
    container.mu.Unlock()
}

有没有一种安全的方法可以做到这一点?这几乎就像我想要一个竞争条件,我不介意它是否被多次写入,但这样做的第一个线程应该阻止后续读取这样做。相当新的东西和一般的并发性。

【问题讨论】:

  • 调用代码是否首先需要为此负责?也许在返回实例之前公开一个构造函数NewContainer() *container 会更容易。

标签: go concurrency


【解决方案1】:

在您的方法中,您必须先锁定,然后才能检查是否已设置 Items。例如:

func (container *Container) loadContainer() {
    container.mu.Lock()
    defer container.mu.Unlock()

    if container.Items != nil {
        return
    }

    container.Items = loadItems()
}

但是sync.Once 提供了更好更快的选择。隐藏Items 字段也是明智之举,这样就不会被意外引用。导出的函数应该负责初始化,并返回值,只初始化一次:

type Container struct {
    once  sync.Once
    items []Item
}

func (container *Container) GetItems() []Item {
    container.once.Do(func() {
        container.Items = loadItems()
    })
    return container.items
}

【讨论】:

  • 谢谢,这非常有帮助!快速提问:有没有一种很好的方法来重置一次错误(假设 loadItems 抛出错误?)
  • sync.Once 不支持报告失败也不支持“reset”。
猜你喜欢
  • 2020-12-15
  • 2020-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多