Как отменить отложенный оператор в Go? - PullRequest
0 голосов
/ 16 апреля 2020

У меня есть следующая структура кода, где я Lock () в точке A, и мне нужно обязательно разблокировать () в точке B в той же функции. Между точками A и B у меня есть несколько возвратов, основанных на ошибках, где мне нужно было бы разблокировать (). Использование defer lock.Unlock() в точке A решает проблему, заключающуюся в том, что в случае ошибок блокировка будет снята. Однако, если выполнение успешно достигает точки B - как я могу отменить эту отсрочку и Unlock () в любом случае?

func foo() {
    ...
    // point A
    lock.Lock()
    defer lock.Unlock()
    ...
    err := bar()
    if err != nil {
        return
    }
    ...
    //point B - need to definetely unlock here
    //lock.Unlock() ?
}

Ответы [ 2 ]

4 голосов
/ 16 апреля 2020

Невозможно отменить отложенную функцию.

Используйте локальную переменную, чтобы записать состояние функции относительно блокировки и проверить эту переменную в отложенной функции.

lock.Lock()
locked := true

defer func() {
    if locked  {
        lock.Unlock()
    }
}()
...
err := bar()
if err != nil {
    return
}
...

lock.Unlock()
locked = false

...

Поскольку блокировки обычно используются в многопоточной среде, следует отметить, что локальная переменная функции locked должна быть доступна только для одной процедуры (спасибо, Рик-777, за то, что вызвал это в комментарии).

3 голосов
/ 16 апреля 2020

Вы не можете отменить отложенную функцию.

Вы можете использовать sync.Once, чтобы гарантировать, что мьютекс разблокирован ровно один раз:

func foo() {
    var unlockOnce sync.Once

    // point A
    lock.Lock()
    defer unlockOnce.Do(lock.Unlock)
    ...
    err := bar()
    if err != nil {
        return
    }
    ...
    // point B - need to unlock here
    unlockOnce.Do(lock.Unlock)
}

Если возможно, возможно, лучше провести рефакторинг вашего кода так, чтобы заблокированная часть оставалась в одной функции:

func fooLock() error {
    lock.Lock()
    defer lock.Unlock()
    if err := bar(); err != nil { return err }
    ...
    return nil
}

func foo() {
    if err := fooLock(); err != nil { return }
    ... do the bit that doesn't need a lock
}

Очевидно, что именование и обработка ошибок здесь неэффективны, потому что код является обобщенным c и не специфицируется c. Если вам нужна информация из блока кода перед точкой B, которая теперь находится в коде в fooLock, она может быть возвращена вместе с ошибкой.

...