Ответ
Принцип работы с инициализированными глобальными переменными (хранящимися в сегменте .data
) абсолютно идентичен работе с неинициализированными (из .bss
). Все горутины разделяют общее адресное пространство, и доступ к этим переменным должен быть синхронизирован.
- Сегмент
.data
содержит глобальные и статические переменные, которым присвоены начальные значения в коде. - Общий доступ: Все горутины имеют доступ к этим переменным.
- Риск состояния гонки: Одновременная несинхронизированная запись или смешанное чтение/запись из нескольких горутин приведет к состоянию гонки.
Пример (проблема):
package main
import (
"fmt"
"sync"
)
// Переменная инициализирована значением 100
var sharedData = 100 // Находится в сегменте .data
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
sharedData = 200 // Запись из первой горутины
}()
go func() {
defer wg.Done()
sharedData = 300 // Запись из второй горутины
}()
wg.Wait()
// Результат непредсказуем: может быть 200 или 300
fmt.Println(sharedData)
}
Пример (решение с sync.Mutex
):
package main
import (
"fmt"
"sync"
)
var sharedData = 100
var mu sync.Mutex // Мьютекс для защиты sharedData
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
mu.Lock()
sharedData = 200
mu.Unlock()
}()
go func() {
defer wg.Done()
mu.Lock()
sharedData = 300
mu.Unlock()
}()
wg.Wait()
// Результат предсказуем и зависит от того, какая горутина первой захватит мьютекс
fmt.Println(sharedData)
}
Вывод: Неважно, была ли глобальная переменная инициализирована или нет. Если она изменяется несколькими горутинами, доступ к ней обязательно должен быть защищен примитивами синхронизации.