Ответ
В Go, для обеспечения потокобезопасности доступа к динамическому массиву (слайсу), используйте примитивы синхронизации из пакета sync
, такие как sync.Mutex
или sync.RWMutex
.
Пример использования sync.RWMutex
для SafeArray
(слайса):
package main
import (
"fmt"
"sync"
)
type SafeArray struct {
mu sync.RWMutex
items []int
}
// Append добавляет элемент в слайс потокобезопасно.
func (s *SafeArray) Append(item int) {
s.mu.Lock() // Блокировка для записи
defer s.mu.Unlock()
s.items = append(s.items, item)
}
// Get возвращает элемент по индексу потокобезопасно.
func (s *SafeArray) Get(index int) (int, bool) {
s.mu.RLock() // Блокировка для чтения
defer s.mu.RUnlock()
if index < 0 || index >= len(s.items) {
return 0, false
}
return s.items[index], true
}
// Len возвращает текущую длину слайса потокобезопасно.
func (s *SafeArray) Len() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.items)
}
func main() {
sa := SafeArray{items: []int{}}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(val int) {
defer wg.Done()
sa.Append(val)
}(i)
}
wg.Wait()
fmt.Printf("Длина SafeArray: %dn", sa.Len())
val, ok := sa.Get(50)
if ok {
fmt.Printf("Элемент по индексу 50: %dn", val)
}
}
Ключевые моменты:
sync.Mutex
: Используется для эксклюзивного доступа (один горутина может читать или писать в любой момент). Подходит, когда операции чтения и записи примерно одинаково часты или когда операции записи требуют полной изоляции.sync.RWMutex
: Позволяет множеству горутин читать одновременно, но только одной горутине писать. Это более эффективно для сценариев с частым чтением и редкой записью, так как чтение не блокирует другие операции чтения.Lock()
/Unlock()
: Используются для операций записи (например,Append
).RLock()
/RUnlock()
: Используются для операций чтения (например,Get
,Len
).defer
: Гарантирует, что мьютекс будет разблокирован, даже если произойдет паника или ошибка, предотвращая дедлоки.- Важно: Мьютекс должен защищать доступ к данным, а не сами данные. Все операции, изменяющие или читающие защищаемые данные, должны выполняться внутри заблокированного участка кода. Не забывайте, что возвращаемые из
Get
значения являются копиями, но если бы вы возвращали указатель на сложную структуру, то её внутреннее состояние могло бы быть изменено извне без защиты мьютексом.