何时可以安全地访问互斥锁保护变量而不锁定?

时间:2015-07-03 04:37:00

标签: concurrency go synchronization race-condition

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

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

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

这是真的,在一些准确的“时间点”之后,写入从缓存刷新到内存并且保证不再需要写入,读取实际上可以不使用RWMutex.RLock吗?如果是,何时是无锁访问之前的时间点或如何设置条件?

2 个答案:

答案 0 :(得分:1)

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

请注意,我的初步答案是为Freeze()实施Register操作的提案,但后来我意识到正确的实现不会比使用RWLock更快。

答案 1 :(得分:1)

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

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

  1. 获取"更新锁定"互斥。
  2. 制作地图的副本。您希望手动复制所有键/值:简单分配不起作用,因为地图是引用类型。
  3. 对地图副本进行更改。
  4. 使用StorePointer包中的sync/atomic以原子方式更新指向实时地图的指针,以指向新地图。
  5. 释放互斥锁。
  6. 在(4)中的原子更新之前运行的所有内容都将看到旧地图,之后的所有内容都将看到新地图。这些goroutine绝不会从正在写入的地图中读取,因此不需要RWMutex