Ответ
Для потокобезопасного инкремента (увеличения) счетчика внутри структуры в Go можно использовать sync.Mutex
или пакет sync/atomic
.
1. Использование sync.Mutex
(для более сложных операций или когда счетчик является частью более крупного состояния):
package main
import (
"fmt"
"sync"
)
type CounterMutex struct {
mu sync.Mutex
count int
}
func (c *CounterMutex) Increment() {
c.mu.Lock() // Блокируем доступ к счетчику
defer c.mu.Unlock() // Гарантируем разблокировку
c.count++
}
func (c *CounterMutex) Get() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
func main() {
counter := CounterMutex{}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Increment()
}()
}
wg.Wait()
fmt.Printf("Счетчик с Mutex: %dn", counter.Get())
}
2. Использование sync/atomic
(для простых, атомарных операций, таких как инкремент/декремент):
Пакет sync/atomic
предоставляет низкоуровневые, высокопроизводительные примитивы для атомарных операций над базовыми типами данных (например, int32
, int64
, uint32
, uint64
, Pointer
). Они обычно быстрее, чем мьютексы, для простых операций, так как не требуют блокировки и разблокировки, используя аппаратные инструкции процессора для атомарности.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type CounterAtomic struct {
count int64 // Для atomic операций часто используются int64/uint64
}
func (c *CounterAtomic) Increment() {
atomic.AddInt64(&c.count, 1) // Атомарно увеличиваем значение на 1
}
func (c *CounterAtomic) Get() int64 {
return atomic.LoadInt64(&c.count) // Атомарно читаем значение
}
func main() {
counter := CounterAtomic{}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Increment()
}()
}
wg.Wait()
fmt.Printf("Счетчик с Atomic: %dn", counter.Get())
}
Выбор между Mutex
и atomic
:
sync.Mutex
: Используйте, когда вам нужно защитить более сложные операции, включающие несколько шагов, или когда счетчик является частью более крупного состояния, которое должно быть изменено атомарно.Mutex
обеспечивает более общую защиту.sync/atomic
: Идеально подходит для простых, одиночных операций, таких как инкремент, декремент, загрузка или сохранение значения. Он обеспечивает лучшую производительность за счет использования аппаратных инструкций процессора для атомарности, но применим только к базовым типам данных и простым операциям.