Двойная проверка блокировки в golang - Почему mutex.RLock () требуется? - PullRequest
0 голосов
/ 08 января 2019

У меня есть фрагмент кода с этого веб-сайта , который дважды проверил блокировку для инициализации объекта.

func checkSyncProducer() {
    mutex.RLock()
    if syncProducer == nil {
        mutex.RUnlock()
        mutex.Lock()
        defer mutex.Unlock()
        if syncProducer == nil {
            syncProducer  = createSyncKafkaProducer() //this func will initialize syncProducer.
        }
    } else {
        defer mutex.RUnlock()
    }
}

Этот фрагмент кода имеет mutex.RLock() до первой проверки на ноль.

Почему это требуется? (это объясняется на странице, но я не мог понять) И это не добавляет накладных расходов, потому что каждый раз, когда checkSyncProducer вызывается, блокировка чтения будет снята и снята.

Должна ли быть еще одна нулевая проверка перед установкой блокировки чтения, например:

func checkSyncProducer() {
    if syncProducer == nil {
        mutex.RLock()
        if syncProducer == nil {
            mutex.RUnlock()
            mutex.Lock()
            defer mutex.Unlock()
            if syncProducer == nil {
                createSyncKafkaProducer()
            }
        } else {
            defer mutex.RUnlock()
        }
    }
}

Первая нулевая проверка гарантирует, что RLock не будет использован без необходимости. Я прав?

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Если вы не получите RLock для чтения syncProducer, это гонка данных, поскольку другая программа может обновить ее.

Если вы предполагаете, что чтение / запись в переменные-указатели являются атомарными, это выглядит безвредным - быстрое чтение syncProducer никогда не приведет к некорректному поведению. Если вы не сделаете это атомарное предположение, чтение может выдать только некоторые байты указателя, если вам не повезло, и ваша программа потерпит крах.

Это может быть или не быть возможным в зависимости от используемой архитектуры, размера машинного слова, версии компилятора и т. Д. Но RLock избегает любой заботы.

Вместо того, чтобы явно возиться с RWLocks, вероятно, лучше использовать это (предполагая, что цель состоит в том, чтобы лениво инициализировать переменную):

var (
    syncOnce sync.Once
    syncProducerInternal *syncProducerType
)

func syncProducer() *syncProducerType {
    syncOnce.Do(func() { syncProducerInternal = createSyncKafkaProducer() })
    return syncProducerInternal
}

Тогда код, которому нужен производитель синхронизации, может вызвать функцию syncProducer(), чтобы получить его, и никогда не видеть нулевой указатель.

0 голосов
/ 08 января 2019

Требуется, потому что проверка доступна только для чтения. Вместо этого было бы возможно сделать блокировку RW, но это означало бы, что только одна процедура за раз могла выполнить проверку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...