Модульні уроки (4/5)
`sync.Mutex` і `sync.WaitGroup`
Канали є основною ідіомою Go, але іноді більш традиційною
потрібні примітиви. Пакет sync надає м’ютекси, зачекайте
групи та інші утиліти для координації спільного стану.
sync.Mutex: ексклюзивне блокування
import "sync"
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *Counter) Get() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}Лише одна gor-програма за раз входить між Lock() та Unlock().
Обов’язковий шаблон: defer mu.Unlock() одразу після Lock().
sync.RWMutex: багато читачів, один автор
Для інтенсивного читання:
var mu sync.RWMutex
mu.RLock() // read lock: più goroutine simultanee OK
v := data
mu.RUnlock()
mu.Lock() // write lock: esclusivo
data = newValue
mu.Unlock()Корисно, лише якщо читання дійсно домінує над записом; інакше регулярний
sync.Mutex простіший і часто швидший завдяки нижчому внутрішньому
суперечка.
sync.WaitGroup: очікування N gor-програм
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
work(id)
}(i)
}
wg.Wait() // blocca finché tutti i Done() sono arrivatiРобочий процес:
wg.Add(N)збільшує лічильник на N (перед запуском goroutines).- Після завершення кожна gor-програма викликає
wg.Done()(в ідеалі черезdefer). wg.Wait()блокується, доки лічильник не повернеться до 0.
sync.Once: потокобезпечна відкладена ініціалізація
var (
once sync.Once
config *Config
)
func GetConfig() *Config {
once.Do(func() {
config = loadConfig()
})
return config
}Зворотний виклик у once.Do виконується рівно один раз, навіть під час одночасного
дзвінки. Шаблон ініціалізації Singleton/lady.
Mutex проти каналу: коли що використовувати
Інструкції (також з офіційних поширених запитань Go):
| Справа | Бажаний інструмент |
|---|---|
| Передача права власності на дані | канал |
| Розподіл роботи (черга роботи) | канал |
| Координація незалежних підпрограмм | канал |
| Захист поля структури | Mutex |
| Кеш/лічильник | Mutex або атомарний |
| Посилання на Singleton | sync.Once |
«Канали для оркестровки, м’ютекси для спільних даних» — це гарне емпіричне правило.
Детектор гонок
Виконайте тести за допомогою -race, щоб виявити конкуренцію даних:
go test -race ./...
go run -race main.goДетектор перегонів інструментує двійковий файл і реєструє кожну несинхронізовану інформацію доступ до спільної пам'яті. Це інструмент №1 для перевірки одночасного коду до виробництва.
Спробуйте
Захистіть приріст лічильника за допомогою mu.Lock() + defer mu.Unlock().
Показати підказку
defer mu.Unlock() одразу після Lock() гарантує звільнення навіть у разі паніки.
Рішення доступне після 3 спроб
Запустіть 3 горутини та дочекайтеся їх усіх за допомогою sync.WaitGroup (Add(3), Done() у кожній горутині, Wait()).
Показати підказку
Додайте ПЕРЕД запуском goroutine; Виконано ВСЕРЕДИНІ goroutine з відкладенням.
Рішення доступне після 3 спроб
Який рекомендований шаблон, щоб гарантувати звільнення м’ютексу?
mu.Lock()
// ?Підведення підсумків
sync.Mutexзахищає спільний стан; завждиdefer mu.Unlock().- Використовуйте приймач вказівника, щоб уникнути копіювання мьютекса.
sync.RWMutexдля шаблонів багато читачів/один запис (лише якщо це допомагає).sync.WaitGroup:Addдоgo,Doneзdefer,Waitчекати.sync.Once: потокобезпечна відкладена ініціалізація.- "Канали для оркестровки, м'ютекси для спільних даних".
go test -race: інструмент №1 для виявлення перегонів даних.