【问题标题】:How to unlock Mutex twice如何两次解锁互斥锁
【发布时间】:2019-08-29 00:53:13
【问题描述】:

两次解锁互斥锁是否安全? 我的代码:

var m sync.RWMutex = sync.RWMutex{}

func Read() {
    m.RLock()
    defer m.RUnlock()

    // Do something that needs lock
    err := SomeFunction1()
    if err != nil {
        return
    }

    m.RUnlock()

    // Do something that does not need lock
    SomeFunction2()

}

我需要defer m.RUnlock() 来处理SomeFunction1() 返回错误的情况。但是当SomeFunction1()无误返回时,m会被m.RUnlock()defer m.RUnlock()解锁两次。

两次解锁互斥锁是否安全?如果没有,我应该如何修复我的代码?

【问题讨论】:

  • 请注意,当您发现自己想要执行此操作(在函数中的各个奇怪点锁定和解锁互斥锁)时,通常表明您的代码不是真的结构合理。不过,在一些奇怪的情况下这是合适的。

标签: go


【解决方案1】:

两次解锁互斥锁是否安全?

不,您不应该两次解锁互斥锁。根据docs,这是一个运行时错误

RUnlock 撤消单个 RLock 调用;它不会影响其他同时阅读的读者。如果 rw 在进入 RUNlock 时没有被锁定以读取,这是一个运行时错误。


如果没有,我应该如何修复我的代码?

我建议保留defer,但只保留m.RUnlock(),以防出错。如果您在 SomeFunction1()SomeFunction2() 之间添加更多函数调用,这可以轻松扩展。

func Read() {
    var err error
    m.RLock()
    defer func() {
        if err != nil {
            m.RUnlock()
        }
    }()
    

    // Do something that needs lock
    err = SomeFunction1()
    if err != nil {
        return
    }

    m.RUnlock()

    // Do something that does not need lock
    SomeFunction2()
}

试试Go Playground

【讨论】:

    【解决方案2】:

    解锁未锁定的 Mutex 会导致恐慌。

    您可以简单地删除 defer 并将其添加到 if 条件中:

    var m sync.RWMutex = sync.RWMutex{}
    
    func Read() {
        m.RLock()
    
        // Do something that needs lock
        err := SomeFunction1()
        if (err != nil) {
            m.RUnlock()
            return
        }
    
        m.RUnlock()
    
        // Do something that does not need lock
        SomeFunction2()
    }
    

    甚至更好(对可读性的影响很小):

    func Read() {
        m.RLock()
    
        err := SomeFunction1()
        m.RUnlock()
        if (err != nil) {
            return
        }
    
        SomeFunction2()
    }
    

    【讨论】:

      【解决方案3】:

      来自 godoc:

      如果 m 在进入 Unlock 时没有被锁定,这是一个运行时错误。

      所以,不要那样做。

      最简单的解决方法是不使用延迟解锁,而是在每个可能的出口处解锁。或者,您也可以这样做,但不容易阅读:

      func Read() {
          if err := func() {
            m.RLock()
            defer m.RUnlock()
      
            // Do something that needs lock
            err := SomeFunction1()
            if err != nil {
                return err
            }(); err != nil {
              return err
           }
      
          // Do something that does not need lock
          SomeFunction2()
      
      }
      

      【讨论】:

        猜你喜欢
        • 2018-05-23
        • 2022-07-31
        • 1970-01-01
        • 2010-11-22
        • 1970-01-01
        • 2021-12-23
        • 2013-02-11
        • 2012-12-25
        相关资源
        最近更新 更多