Ответ
Существует несколько подходов для обеспечения потокобезопасного доступа к полям структуры. Выбор зависит от гранулярности блокировок и специфики задачи.
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)
}