понедельник, 30 марта 2020 г.

Блокировка взаимного исключения (мьютекс) в Golang

Мьютексы позволяют синхронизировать доступ к данным путем явной блокировки, без каналов.

Иногда удобнее синхронизировать доступ к данным с помощью явной блокировки вместо использования каналов. Для этой цели стандартная библиотека Go предлагает блокировку взаимного исключения sync.Mutex.

Чтобы этот тип блокировки был безопасным, очень важно, чтобы все обращения к совместно используемым данным, как чтение, так и запись, выполнялись только тогда, когда goroutine удерживает блокировку (lock). Одной ошибки одной goroutine достаточно, чтобы ввести гонку данных и сломать программу.

Из-за этого вам следует подумать о разработке пользовательской структуры данных с чистым API и убедиться, что вся синхронизация выполняется внутри.

В следующем примере мы создаем безопасную и простую в использовании конкурентную структуру данных AtomicInt, которая хранит одно целое число. Любое количество goroutine может безопасно получить доступ к этому числу с помощью методов Add и Value.

// AtomicInt - это конкурентная структура данных, 
// которая хранит int.
// Его нулевое значение равно 0.
type AtomicInt struct {
    mu sync.Mutex // Блокировка, которая может удерживаться одной goroutine за раз.
    n  int
}

// Add добавляет n к AtomicInt 
// как отдельная атомарная операция.
func (a *AtomicInt) Add(n int) {
    a.mu.Lock() // Дожидаемся освобождения блокировки (lock), а затем берем ее.
    a.n += n
    a.mu.Unlock() // Снимаем блокировку (lock).
}

// Value возвращает значение a.
func (a *AtomicInt) Value() int {
    a.mu.Lock()
    n := a.n
    a.mu.Unlock()
    return n
}

func main() {
    wait := make(chan struct{})
    var n AtomicInt
    go func() {
        n.Add(1) // один доступ
        close(wait)
    }()
    n.Add(1) // другой конкурентный доступ
    <-wait
    fmt.Println(n.Value()) // 2
}


Читайте также:


Комментариев нет:

Отправить комментарий