Вы не можете получить доступ к map
сразу из нескольких подпрограмм.
Это может работать в течение некоторого времени, но обязательно приведет к сбою или вызвать неожиданные результаты.
Mutex гарантирует, что только одна процедура go может работать с фрагментом кода между Lock
и Unlock
одновременно.
В качестве альтернативы вы можете использовать sync.Map
, который является поточно-ориентированным для чтения и записи.
m := new(sync.Map)
go func() {
for r := 0; true; r++ {
m.Store(r, r)
time.Sleep(time.Millisecond)
}
}()
go func() {
for r := 0; true; r++ {
res, ok := m.Load(r)
if ok {
fmt.Println(res)
}
time.Sleep(10 * time.Millisecond)
}
}()
sync.Map не всегда означает потокобезопасность
При sync.Map
чтение (загрузка) и запись (сохранение) являются атомарными, то есть вызов их сразу из разных подпрограмм go будет работать как ожидалось / не повредит данные или не выдаст ошибку.
Однако создание разных sync.Map
может быть не атомарным и, следовательно, не поточно-ориентированным.
Например,
val, ok := m.Load("someKey")
if !ok {
m.Store("someKey", LoadData())
}
Если этот код запускается из разных подпрограмм go одновременно, есть вероятность, что обе подпрограммы go войдут в оператор if
и загрузят данные, даже если они не были предназначены.
Поэтому иногда вам может понадобиться использовать мьютексы вместо sync.Map
val, ok := m.Load("someKey")
if !ok {
mutex.Lock()
defer mutex.Unlock()
val, ok = m.Load("someKey")
if !ok {
m.Store("someKey", LoadData())
}
}