Go中提前解锁已defer的RWMutex读锁的最佳实践是什么?
方案1:作用域隔离(优先推荐)
把需要持有读锁的逻辑封装到独立的匿名函数中,锁的生命周期完全绑定该函数的作用域,天然避免漏解锁、重复解锁问题。
func (s *Structure) Get(key interface{}, object interface{}) (found bool, err error ){ // 把需要加锁的逻辑全部收敛到匿名闭包内 var value []byte var path []byte // 匿名函数内的逻辑只要退出就会触发defer解锁,不需要关心内部有多少返回分支 err = func() error { s.RLock() defer s.RUnlock() ref, err := s.Index.GetOneEquals(string(s.StructName), key) if err != nil { return err } path = ref.ToPath(s.StructName) if path == nil { return nil } value, err = dbdrivers.DB.Get(path) return err }() // 执行到这里锁已经被自动释放了 if err != nil { return false, err } if path == nil { return false, nil } err = encoding.Marshaler.Unmarshal(value, object) if err != nil { return false, err } return true, nil }
该方案优势:锁的范围完全由闭包边界限定,后续迭代修改加锁段逻辑时,不需要额外维护解锁分支,不存在漏解锁、重复解锁风险,代码可读性更高。
方案2:标识位控制defer逻辑
如果不想拆分匿名函数,可以用布尔变量控制defer的解锁行为:
func (s *Structure) Get(key interface{}, object interface{}) (found bool, err error ){ s.RLock() locked := true // defer只有在locked为true时才执行解锁 defer func() { if locked { s.RUnlock() } }() ref, err := s.Index.GetOneEquals(string(s.StructName), key) if err != nil { return false, err } path := ref.ToPath(s.StructName) if path == nil { return false, nil } value, err := dbdrivers.DB.Get(path) if err != nil { return false, err } // 提前解锁,先把标识位设为false,避免defer重复解锁 locked = false s.RUnlock() err = encoding.Marshaler.Unmarshal(value, object) if err != nil { return false, err } return true, nil }
该方案注意点:每次主动解锁前必须先修改locked标识位,否则还是会出现重复解锁问题。
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

