Ответ
Нет, стандартный тип map в Go не является потокобезопасным. Одновременная запись и чтение из map разными горутинами приведет к состоянию гонки (race condition), что может вызвать панику приложения или привести к непредсказуемым результатам.
Для обеспечения потокобезопасности при работе с map используются следующие подходы:
-
sync.Mutexилиsync.RWMutex: Классический способ защиты с помощью мьютекса.RWMutexпредпочтительнее, когда количество чтений значительно превышает количество записей, так как он позволяет выполнять множество чтений одновременно.Пример с
sync.RWMutextype SafeMap struct { mu sync.RWMutex m map[string]int } func (s *SafeMap) Get(key string) (int, bool) { s.mu.RLock() // Блокировка на чтение defer s.mu.RUnlock() val, ok := s.m[key] return val, ok } func (s *SafeMap) Set(key string, value int) { s.mu.Lock() // Блокировка на запись defer s.mu.Unlock() s.m[key] = value } -
sync.Map: Специализированный тип, добавленный в Go 1.9. Он оптимизирован для сценариев, где ключ после записи редко изменяется или удаляется, а количество чтений велико.sync.Mapпозволяет избежать глобальной блокировки для большинства операций чтения, что делает его производительнееmapс мьютексом в определенных условиях.
Ответ 18+ 🔞
А, ну ты про это, про мапы в гоурутинах! Да, тут история простая, как три копейки, но если накосячить — будет пиздец, а не программа.
Смотри, обычная мапа в Go — это как тарелка с оливье на новогоднем столе, к которой лезут десять пьяных дядек. Один вилку ткнул, другой уже ложкой херачит, третий вообще рукой лезет. В итоге — половина салата на полу, майонез на шторах, а кто-то в итоге получит в морду вилкой. Так и с мапой: если несколько горутин начинают в неё писать и читать без спросу, runtime может просто охуеть и выдать панику fatal error: concurrent map read and map write. И всё, приехали.
Что делать-то? Варианта два, оба с приколами.
Первый — классика, мьютексы. Берёшь sync.RWMutex, оборачиваешь им свою мапу в структуру и делаешь методы. Это как поставить бугая у той самой тарелки с оливье. Хочешь взять — спросись, хочешь положить — тем более. RWMutex хорош тем, что пока никто не пишет, много народу может читать одновременно. Но как только кто-то собрался записать — все читающие ждут, писатель один работает. В коде это выглядит примерно так:
type SafeMap struct {
mu sync.RWMutex // Вот наш бугай, охраняющий порядок
m map[string]int
}
func (s *SafeMap) Get(key string) (int, bool) {
s.mu.RLock() // Бугай, я только посмотреть! (блокировка на чтение)
defer s.mu.RUnlock() // Посмотрел, отпускаю
val, ok := s.m[key]
return val, ok
}
func (s *SafeMap) Set(key string, value int) {
s.mu.Lock() // Бугай, я сейчас новую селёдку под шубой добавлю! (блокировка на запись)
defer s.mu.Unlock()
s.m[key] = value
}
Работает надёжно, проверено веками. Но если у тебя операции с мапой — это адский хотспот, где все горутины только тем и заняты, что пишут и читают, то бугай-мьютекс может стать узким горлышком. Все будут ждать своей очереди к тарелке, а праздник уже кончился.
Второй вариант — sync.Map. Это уже не просто тарелка, а какая-то хитрая японская штука с конвейером. Его добавили в Go 1.9 для очень конкретных сценариев. Он охуенно быстр, когда у тебя:
1) Ключи в основном пишутся один раз, а потом только читаются (типа кэша конфигурации).
2) Или когда у тебя кучу горутин работают с совершенно разными наборами ключей.
Внутри он умный, там нет одной большой блокировки на всю мапу, поэтому в этих специфичных случаях он летает. Но! Он идиоматически не такой, как обычная мапа. Методы другие: Load, Store, LoadOrStore, Range. И если тебе нужны, например, длина мапы или итерация по всем ключам — с sync.Map это не так просто.
var m sync.Map
m.Store("answer", 42) // Записали
value, ok := m.Load("answer") // Прочитали
Короче, выбор такой:
map + sync.RWMutex— твой универсальный солдат, понятный, предсказуемый. Подходит для большинства случаев. Если не уверен — бери его.sync.Map— это спецназ. Бери только если точно понимаешь, что у тебя один из тех самых узких сценариев, где он выжмет все соки. Иначе можешь получить хуже производительность и головную боль.
Вот и вся магия, ёпта. Главное — не лезь в общую мапу из разных горутин без защиты, а то будет не "му-му", а полный пиздец.