我对一个接一个的互斥锁锁定/解锁有点困惑。我使用的是一个RWMutex,当然,所有的goroutine都会有相同的互斥量。
当经常使用互斥锁时,这段代码仍然是竞态保护的吗?
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...
}我会想到的示例情况可能会发生:
falsetrue >G219db >或者在这种情况下,我必须一次性锁定/解锁吗?
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
// ...
}发布于 2018-08-25 04:00:45
欢迎来到同步世界。您的评估是正确的,第一个实现可能会出现并发问题。对于第二个,这些并发问题被移除,但它是完全锁定的,甚至没有并发读访问的机会。你不必这样做,你可以做一个读锁的初始检查,然后如果检查确定需要创建,建立一个写锁,然后重新检查,如果仍然需要创建,然后解锁。这并不是一个不寻常的构造。它的效率较低(由于执行两次检查),因此您可以决定权衡,主要是基于执行两次检查的代价,以及函数能够在只读路径中操作的频率。
https://stackoverflow.com/questions/52010914
复制相似问题