Мьютексы позволяют синхронизировать доступ к данным путем явной блокировки, без каналов.
Иногда удобнее синхронизировать доступ к данным с помощью явной блокировки вместо использования каналов. Для этой цели стандартная библиотека 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
}
Читайте также:
- Каналы в Golang предлагают синхронизированное общение
- Эффективный Go: параллелизм, go-процедуры (goroutines)
- Основы Go: sync.Mutex
Комментариев нет:
Отправить комментарий