【问题标题】:When can safely access mutex protected variable without locking?什么时候可以在不锁定的情况下安全地访问互斥保护变量?
【发布时间】:2015-07-03 04:37:00
【问题描述】:

在我的代码中存储配置的常见模式是受 RWMutex 保护的“map[string]interface{}”,但通常在应用程序启动后(可以在多个 go-routine 中触发),地图变得完全只读。所以我有一种感觉,从某个时间点开始,读取时的 RWMutex 应该是不必要的。

此配置映射的示例位于 http://play.golang.org/p/tkbj9DBok_

让我想到这一点的一个事实是,在一些生产代码中,它实际上是在以这种方式对共享对象进行不受保护的访问(尽管它在初始化后大多是只读的),我理解使用 RWMutex 保护的正常方式,但有趣的是这个格式错误的代码在过去几个月里没有遇到问题。

在某个准确的“时间点”将写入从缓存刷新到内存并保证不再需要写入之后,是否真的可以在没有 RWMutex.RLock 的情况下进行读取?如果是,无锁访问前的时间点或如何设置条件?

【问题讨论】:

    标签: concurrency go synchronization race-condition


    【解决方案1】:

    只要没有人修改地图,多线程一次读取它应该是安全的。不幸的是,如果没有任何锁定,当您想要更新地图时,您将无法确保没有其他人正在阅读地图。

    因此,一种解决方案是永远不要更新地图,而是自动替换它。 read-copy-update 算法可以在这里使用。而不是直接访问地图,因此您需要取消引用指针才能访问地图。要更新它,您可以执行以下操作:

    1. 获取“更新锁”互斥锁。
    2. 复制地图。您想手动复制所有键/值:简单的分配不起作用,因为映射是引用类型。
    3. 对地图副本进行更改。
    4. 使用sync/atomic 包中的StorePointer 自动更新指向实时地图的指针以指向您的新地图。
    5. 释放互斥锁。

    在 (4) 中的原子更新之前运行的所有内容都将看到旧地图,之后的所有内容都会看到新地图。这些 goroutine 绝不会从正在写入的地图中读取数据,因此不需要 RWMutex

    【讨论】:

    • 我相信在这种情况下您将不得不使用LoadPointer 来读取地图。
    • 但我仍然不确定的一件事是 RWMutex.RLock(纯读取的原子添加)和原子 LoadPointer 之间可能的开销比较。我知道这在大多数系统中非常微不足道,但不确定是否要完全摆脱它们。
    • @kostya:如果我们要完全释放锁定,那么LoadPointer 将是必要的,是的。使用互斥锁保护更新,它应该是没有必要的。 StorePointer 是为了读者的利益。
    • @kostya:在某种意义上存在一种竞赛,即读者将看到指针的旧版本或新版本,但他们永远不会看到地图的中间版本。如果您需要更多的保证,那么也许 RWLock 更合适。
    • 请注意,sync/atomic 包提供的atomic.Value 可能比atomic.StorePointer 更容易/更好,具体取决于您正在做什么(例如,文档包括example showing a safe rarely modified map,通过写入时复制) .
    【解决方案2】:

    RLockRUnlock 是非常快速的操作。如果没有编写器,它们基本上是无锁的,每个只需要 1 个原子操作 (http://golang.org/src/sync/rwmutex.go?h=RLock#L29)。所以除非你的应用程序因为读取配置很慢而表现不佳,否则我建议使用RWLock

    请注意,我最初的回答是建议为 Register 实施 Freeze() 操作,但后来我意识到正确的实施不会比使用 RWLock 快。

    【讨论】:

    • 感谢您的快速评论。关键是启动(写入)可能是并发的,并且没有进行“冻结”的好地方。但最终写入将完成,仅此而已。最初,我正在考虑主动等待 10 秒以保证写入结束并刷新到内存中。但是我想看看我是否错过了什么。
    • 我刚刚意识到我建议的解决方案是错误的(因为只读字段是在没有锁定的情况下读取的)。这只是证明使用锁是使其正确的最佳方法。
    • 描述写入变量何时可见的规则可以在这里找到:golang.org/ref/mem
    猜你喜欢
    • 1970-01-01
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    相关资源
    最近更新 更多