Показаны сообщения с ярлыком mutex в Golang. Показать все сообщения
Показаны сообщения с ярлыком mutex в Golang. Показать все сообщения

пятница, 6 ноября 2020 г.

Go style guides: мьютексы с нулевым значением действительны

Нулевое значение sync.Mutex и sync.RWMutex допустимо, поэтому вам почти никогда не понадобится указатель на мьютекс.

Неудачный вариант:

mu := new(sync.Mutex)
mu.Lock()

Хороший пример:

var mu sync.Mutex
mu.Lock()

Если вы используете структуру по указателю, то мьютекс может быть полем без указателя.

Неэкспортированные структуры, которые используют мьютекс для защиты полей структуры, могут встраивать мьютекс.

type smap struct {
    sync.Mutex // только для неэкспортируемых типов

    data map[string]string
}

func newSMap() *smap {
    return &smap{
        data: make(map[string]string),
    }
}

func (m *smap) Get(k string) string {
    m.Lock()
    defer m.Unlock()

    return m.data[k]
}

Для экспортируемых структур:

type SMap struct {
    mu sync.Mutex

    data map[string]string
}

func NewSMap() *SMap {
    return &SMap{
        data: make(map[string]string),
    }
}

func (m *SMap) Get(k string) string {
    m.mu.Lock()
    defer m.mu.Unlock()

    return m.data[k]
}

Встраивайте частные типы или типы, которые должны реализовывать интерфейс Mutex. Для экспортируемых типов используйте частное поле.


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


понедельник, 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
}


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