在函数结束之前,解锁已经延迟的读解锁RWMutex的最佳实践是什么?

huangapple go评论107阅读模式
英文:

What is the best practice to unlock an already-deferred read-unlock RWMutex before the end of the function?

问题

你想知道解决这个问题的最佳方法。

基本上,我想在函数中尽早解锁读取互斥锁,因为最后一部分是并发安全的。

如果我只是在底部部分(在"RUnlocked should technically be here"处)添加一个s.RUnlock,同时保留延迟语句,如果我理解正确的话,这将导致问题。据我理解,RUnlock通过计数器工作。因此,如果我的程序达到那个点之后("RUnlocked should technically be here"),它将在函数结束时调用s.RUnlock延迟的s.RUnlock。因此,计数器将减少两次,可能会导致灾难。

因此,我能想到的唯一解决办法是这样的-这可能会在以后引发问题,因为我需要考虑函数可能结束的所有地方:

  1. func (s *Structure) Get(key interface{}, object interface{}) (found bool, err error ){
  2. s.RLock()
  3. // Concurrent-dangerous stuff begins
  4. ref, err := s.Index.GetOneEquals(string(s.StructName), key)
  5. if err != nil {
  6. s.RUnlock() // <----
  7. return false, err
  8. }
  9. path := ref.ToPath(s.StructName)
  10. if (path == nil) {
  11. s.RUnlock() // <----
  12. return false, nil
  13. }
  14. value, err := dbdrivers.DB.Get(path)
  15. if err != nil {
  16. s.RUnlock() // <----
  17. return false, err
  18. }
  19. // Concurrent-dangerous stuff ends
  20. // RUnlock should technically be here
  21. s.RUnlock() // <----
  22. err = encoding.Marshaler.Unmarshal(value, object)
  23. if err != nil {
  24. return false, err
  25. }
  26. }

有没有更安全的方法来做到这一点?

英文:

I'm working on a database and I want to know the best way to solve this issue.

Basically, I would like to unlock the read mutex earlier in the function because the last bit is concurrent safe.

  1. func (s *Structure) Get(key interface{}, object interface{}) (found bool, err error ){
  2. s.RLock()
  3. defer s.RUnlock()
  4. // Concurrent-dangerous stuff begins
  5. ref, err := s.Index.GetOneEquals(string(s.StructName), key)
  6. if err != nil {
  7. return false, err
  8. }
  9. path := ref.ToPath(s.StructName)
  10. if (path == nil) {
  11. return false, nil
  12. }
  13. value, err := dbdrivers.DB.Get(path)
  14. if err != nil {
  15. return false, err
  16. }
  17. // Concurrent-dangerous stuff ends
  18. // RUnlock should technically be here
  19. err = encoding.Marshaler.Unmarshal(value, object)
  20. if err != nil {
  21. return false, err
  22. }
  23. }

If I just add an s.RUnlocked at the bottom part (where it says RUnlocked should technically be here) while keeping the deferred statement, if I understand correctly, it will cause issues. As I understand it, RUnlock works via a counter. So if my program reaches beyond that point ("RUnlocked should technically be here"), it will call the s.RUnlocked and also the deferred s.RUnlocked as well when the function ends. So the counter will decrement two times which might cause a disaster.

Therefore, the only solution I can think of is this - which is begging for gotchas down the line because I need to think of everywhere the function can end:

  1. func (s *Structure) Get(key interface{}, object interface{}) (found bool, err error ){
  2. s.RLock()
  3. // Concurrent-dangerous stuff begins
  4. ref, err := s.Index.GetOneEquals(string(s.StructName), key)
  5. if err != nil {
  6. s.RUnlock() // &lt;----
  7. return false, err
  8. }
  9. path := ref.ToPath(s.StructName)
  10. if (path == nil) {
  11. s.RUnlock() // &lt;----
  12. return false, nil
  13. }
  14. value, err := dbdrivers.DB.Get(path)
  15. if err != nil {
  16. s.RUnlock() // &lt;----
  17. return false, err
  18. }
  19. // Concurrent-dangerous stuff ends
  20. // RUnlock should technically be here
  21. s.RUnlock() // &lt;----
  22. err = encoding.Marshaler.Unmarshal(value, object)
  23. if err != nil {
  24. return false, err
  25. }
  26. }

Is there a safer way to do this?

答案1

得分: 1

你可以将内部代码块放入自己的函数中,仍然可以使用defer关键字:

  1. func (s *Structure) Get(key interface{}, object interface{}) (found bool, err error) {
  2. s.RLock()
  3. failed, found, err := func() {
  4. defer s.RUnlock()
  5. ref, err := s.Index.GetOneEquals(string(s.StructName), key)
  6. if err != nil {
  7. return true, false, err
  8. }
  9. // 进行其他操作
  10. return false, found, nil
  11. }()
  12. if failed {
  13. return found, err
  14. }
  15. // 继续函数执行,锁已解除
  16. }

这样做可以确保在函数执行完毕后自动释放锁。

英文:

You can still use defer if you put the inner block into its own function:

  1. func (s *Structure) Get(key interface{}, object interface{}) (found bool, err error ){
  2. s.RLock()
  3. failed, found, err:= func() {
  4. defer s.RUnlock()
  5. ref, err := s.Index.GetOneEquals(string(s.StructName), key)
  6. if err != nil {
  7. return true, false, err
  8. }
  9. // Do stuff
  10. return false, found, nil
  11. }()
  12. if failed {
  13. return found, err
  14. }
  15. // continue function, lock is unlocked
  16. }
  17. </details>

huangapple
  • 本文由 发表于 2021年12月6日 01:14:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/70236655.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定