mutex.Lock 在解锁前 returns 的功能

function with mutex.Lock that returns before unlocking

我需要使用互斥量来读取变量,如果变量为 0,则从函数中 return。不过,这会阻止互斥体解锁。

我知道我可以简单地在 return 之前放一个 mutex.Unlock,但它看起来不太好/不正确。

我什至不能在函数的开头做一个 defer mutex.Unlock() 因为后面的代码需要很多时间才能 运行.

有正确的方法吗?

这是例子:

func mutexfunc() {
    mutex.Lock()
    
    if variable == 0 {
        return
    }
    
    mutex.Unlock()

    // long execution time (mutex must be unlocked)
}

更新:

这是我更喜欢的解决方案:

var mutex = &sync.Mutex{}

var mutexSensibleVar = 0

func main() {
    if withLock(func() bool { return mutexSensibleVar == 1 }) {
        fmt.Println("it's true")
    } else {
        fmt.Println("it's false")
    }

    fmt.Println("end")
}

func withLock(f func() bool) bool {
    mutex.Lock()
    defer mutex.Unlock()

    return f()
}

如果你不能使用 defer,这是你不能在这里做的事情,你必须做显而易见的事情:

func mutexfunc() {
    mutex.Lock()
    
    if variable == 0 {
        mutex.Unlock()
        return
    }
    
    mutex.Unlock()

    // long execution time (mutex must be unlocked)
}

如果互斥量只是为了保护那个变量(也就是说,没有你没有向我们展示的其他代码),你也可以使用 sync/atomic:

func f() {
   if atomic.LoadInt64(&variable) ==0 {
      return
   }
   ...
}

您可以将锁定的部分分离到它自己的功能中。

func varIsZero() bool {
    mutex.Lock()
    defer mutex.Unlock()
    return variable == 0
}

func mutexfunc() {
    if varIsZero() { return }
    ...
}

另一种方法是在 mutexfunc 中使用匿名函数而不是完全独立的函数,但这是个人喜好问题。

还要考虑带有“需要解锁”布尔值的(笨拙但可读的)变体:

func f(arg1 argtype1, arg2 argtype2) ret returntype {
    var needToUnlock bool
    defer func() {
        if needToUnlock {
            lock.Unlock()
        }
    }()
    // arbitrary amount of code here that runs unlocked
    lock.Lock()
    needToUnlock = true
    // arbitrary amount of code here that runs locked
    lock.Unlock()
    needToUnlock = false
    // arbitrary amount of code here that runs unlocked
    // repeat as desired
}

你可以把这样的东西包装成一个类型:

type DeferableLock struct {
    L        Locker
    isLocked bool
}

func (d *DeferableLock) Lock() {
    d.L.Lock()
    d.isLocked = true
}

func (d *DeferableLock) Unlock() {
    d.L.Unlock()
    d.isLocked = false
}

func (d *DeferableLock) EnsureUnlocked() {
    if d.isLocked {
        d.Unlock()
    }
}

func NewDeferableLock(l Locker) *DeferableLock() {
    return &DeferableLock{L: l}
}

您现在可以用 DeferableLock 包裹任何 sync.Locker。在像 f 这样的函数中,使用可延迟包装器来包装锁,然后调用 defer d.EnsureUnlock.

(与 sync.Cond 的任何相似之处完全是故意的。)