gpt4 book ai didi

go - 互斥锁用对了吗?

转载 作者:数据小太阳 更新时间:2023-10-29 03:22:08 24 4
gpt4 key购买 nike

我对 mutex locken/unlocking 的次数接二连三感到有点困惑。我正在使用 RWMutex当然,所有 goroutines 都将具有相同的互斥量。

如此频繁地使用互斥量时,这段代码是否仍然受到竞争保护?

func (r *Redis) RedisDb(dbId DatabaseId) *RedisDb {
r.Mu().RLock()
size := len(r.redisDbs) // A
r.Mu().RUnlock()
if size >= int(dbId) { // B
r.Mu().RLock()
db := r.redisDbs[dbId] // C
r.Mu().RUnlock()
if db != nil { // D
return db
}
}
// E create db...
}

我认为可能发生的示例情况:

  1. gorountine1 和 goroutine2 都在运行这个函数
  2. 都在 A 点,因此变量 size 为 3
  3. 条件 B 对于两个 goroutines 都是 true
  4. 两人同时读C
  5. 两个 goroutine 的变量 db 都是 nil,所以条件 C 是 false
  6. 现在两个 goroutine 都转到 E 并创建相同的数据库 2 次,这很糟糕

或者在这种情况下我必须一次锁定/解锁吗?

func (r *Redis) RedisDb(dbId DatabaseId) *RedisDb {
r.Mu().Lock()
defer r.Mu().Unlock()
size := len(r.redisDbs)
if size >= int(dbId) {
db := r.redisDbs[dbId]
if db != nil {
return db
}
}
// create db...
}

解决方案

func (r *Redis) RedisDb(dbId DatabaseId) *RedisDb {
getDb := func() *RedisDb { // returns nil if db not exists
if len(r.redisDbs) >= int(dbId) {
db := r.redisDbs[dbId]
if db != nil {
return db
}
}
return nil
}

r.Mu().RLock()
db := getDb()
r.Mu().RUnlock()
if db != nil {
return db
}

// create db
r.Mu().Lock()
defer r.Mu().Unlock()
// check if db does not exists again since
// multiple "mutex readers" can come to this point
db = getDb()
if db != nil {
return db
}
// now really create it
// ...
}

最佳答案

欢迎来到同步世界。您的评估是正确的,第一次实现可能会出现并发问题。对于第二个,那些并发问题被消除了,但它被完全锁定,甚至没有机会进行并发读取访问。您不必那样做,您可以使用读锁进行初始检查,然后如果该检查确定需要创建,则建立写锁,然后重新检查,如果有则创建仍然需要,然后解锁。这不是一个不寻常的结构。它的效率较低(由于执行了两次检查)因此您需要权衡取舍,主要取决于执行两次检查的成本以及该功能能够在只读路径。

关于go - 互斥锁用对了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52010914/

24 4 0