Как обеспечить потокобезопасный доступ к полям структуры?

Ответ

Существует несколько подходов для обеспечения потокобезопасного доступа к полям структуры. Выбор зависит от гранулярности блокировок и специфики задачи.

1. Мьютекс на всю структуру (грубая блокировка)

Это самый простой и распространенный подход. Один мьютекс защищает все поля структуры.

  • Плюсы: Простота реализации.
  • Минусы: Может стать узким местом, если разные поля часто и независимо друг от друга изменяются разными горутинами.
type SafeData struct {
    mu   sync.Mutex
    fieldA int
    fieldB string
}

func (d *SafeData) SetA(value int) {
    d.mu.Lock()
    defer d.mu.Unlock()
    d.fieldA = value
}

2. Мьютекс на отдельные поля (гранулярная блокировка)

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

  • Плюсы: Увеличивает параллелизм, так как горутины, работающие с разными полями, не блокируют друг друга.
  • Минусы: Усложняет код и увеличивает потребление памяти.
type GranularSafeData struct {
    muA    sync.Mutex
    fieldA int

    muB    sync.Mutex
    fieldB string
}

3. Атомарные операции

Для полей примитивных типов (int32, int64, uintptr и т.д.) можно использовать пакет sync/atomic. Это самый производительный способ для простых операций, таких как инкремент, сравнение с обменом (CAS) и т.д.

import "sync/atomic"

type AtomicCounter struct {
    value int64
}

func (c *AtomicCounter) Increment() {
    atomic.AddInt64(&c.value, 1)
}