Go 互斥使用对吗?
我对一次又一次地锁定/解锁互斥锁感到有点困惑。 我使用的是a,所有Goroutine当然都有相同的互斥 当经常使用互斥锁时,这段代码仍然受种族保护吗Go 互斥使用对吗?,go,concurrency,mutex,race-condition,goroutine,Go,Concurrency,Mutex,Race Condition,Goroutine,我对一次又一次地锁定/解锁互斥锁感到有点困惑。 我使用的是a,所有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().
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...
}
我认为可能发生的情况如下:
size
为3true
db
对于两个goroutine都是nil,因此条件C是false
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
// ...
}
欢迎来到同步世界。您的评估是正确的,第一次实现可能会出现并发问题。对于第二个,这些并发性问题被删除,但它被完全锁定,甚至没有并发读取访问的机会。您不必这样做,您可以使用读锁进行初始检查,然后如果该检查确定需要创建,则建立写锁,然后重新检查,如果仍然需要创建,然后解锁。这不是一个不寻常的构造。它的效率较低(由于执行了两次检查),因此您可以根据两次检查的成本以及函数能够在只读路径中运行的频率来决定取舍。谢谢,是的,读取路径比创建路径更频繁,你的意思是像我现在发布的代码一样吗?:)