【问题标题】:Is assigning value to a struct atomic in go?是否在 go 中为 struct atomic 赋值?
【发布时间】:2020-07-04 08:08:56
【问题描述】:

我有两个结构 AppConfig

type App struct {
    cfg Config
}
type Config struct {
    dbHost   string
    dbPort   int
    user     string
    password string
}

以及在 App 上定义的两个方法来更新和读取cfg 字段。

func (app *App) UpdateConfig(newCfg Config) {
    app.cfg = newCfg
}

func (app *App) GetConfig() Config {
    return app.cfg
}

如果只有一个 goroutine 正在调用 UpdateConfig 并且多个 goroutine 正在通过 GetConfig 方法读取配置,我应该使用互斥锁保护对 app.cfg 的访问吗?

编辑:阅读器 goroutine 在 for 循环中调用 GetConfig。不需要“立即”查看配置的更新值。读者下次迭代看到cfg的更新值就OK了。

所以我重新表述我的问题:读者是否可以看到部分更新的配置值?

【问题讨论】:

  • 除非你使用sync/atomic,否则没有什么是“原子的”,并发读取和写入始终是竞争条件。
  • 如果您不使用同步/原子或互斥锁,则无法保证读取器 goroutine 何时会在写入发生后看到更改。
  • 至少一个访问是写入的任何并发访问都是竞争条件并且行为未定义。同步访问,例如使用互斥锁。
  • 感谢cmets,我已经编辑了问题。
  • 即使你不需要看到更新的变量,它仍然是一场竞赛。没有安全的数据竞赛。使用sync/atomic 进行读/写。它的工作开销很小——内存屏障,不涉及锁定。读者不会看到部分更新的值。问题是,它可能永远看不到更新后的值。

标签: go


【解决方案1】:

不,如果您异步修改它,您不应该期望看到全部或没有更新的配置。

Go 尽量做到合理(即使它没有正式保证);如果您在字长更新无法撕裂的架构上更新字长对象,那么即使您不同步,您也会看到更新或看不到更新。 (这与 C 和 C++ 不同,其中未定义的行为意味着编译器可以合法地做任何事情)。但是在这里,Config 是一个很大的值,没有便宜的方法可以保证你想要的全有或全无更新(不执行一些同步本身)。

但是是的,您应该使用互斥锁来保护配置。如果争用太多,并且您暂时不介意旧配置,那么您可以定期轮询官方配置并在更改时更新本地副本。

实际上,互斥锁速度很快,而且使用异步正确的代码几乎总是更好,即使它有点慢。例如,如果您没有异步正确的代码,那么即使您的代码按照您的意愿运行,您也无法轻松使用竞争检测器来发现其他真正的问题。

【讨论】:

  • “Go 试图做到合理(即使它没有正式保证它)”——这意味着你应该按原样对待它,未定义的行为,而不是依赖它。即使是一个字大小的值,也不应该接受种族。
  • 如果不需要协调(即“发生在之前”),并且你有字大小的值,仍然使用原子。如果架构不需要它们,它们可以编译为 noops。
猜你喜欢
  • 1970-01-01
  • 2014-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-20
  • 1970-01-01
相关资源
最近更新 更多