Sobes.tech
Middle+

Hoe veilig is het om slices te gebruiken in de context van gelijktijdige uitvoering (Concurrency)?

sobes.tech AI

Antwoord van AI

Het gebruik van slices is niet veilig in de context van gelijktijdige toegang (Concurrency) zonder aanvullende synchronisatie-mechanismen. Meerdere gelijktijdige toegang voor lezen en schrijven of twee gelijktijdige schrijfacties op hetzelfde geheugengebied (dat de basisarray van de slice is) leiden tot data races.

Redenen voor onveiligheid:

  1. Wijziging van de basisarray: Een slice is een weergave van de basisarray. Operaties zoals append, vooral als ze de huidige capaciteit overschrijden, kunnen leiden tot herallocatie en kopiëren van gegevens naar een nieuwe basisarray. Als op dat moment een andere goroutine probeert te lezen of schrijven in de oude of nieuwe basisarray, ontstaat een data race.
  2. Wijziging van de slice-header: De header van de slice (een structuur die een pointer naar de basisarray bevat, samen met de lengte en capaciteit) kan ook worden gewijzigd tijdens operaties zoals append. Gelijktijdig lezen en schrijven aan deze header zonder locks leidt tot ongedefinieerd gedrag.

Mechanismen om veiligheid te garanderen:

  • sync.Mutex / sync.RWMutex: Blokkeert de toegang tot de slice vóór elke lees- of schrijfbewerking. sync.RWMutex is te verkiezen in scenario's met frequente lezingen en zeldzame schrijfbewerkingen.

    import "sync"
    
    type SafeSlice struct {
        mu    sync.RWMutex
        items []int
    }
    
    func (s *SafeSlice) Append(item int) {
        s.mu.Lock() // Blokkeert voor schrijven
        defer s.mu.Unlock()
        s.items = append(s.items, item)
    }
    
    func (s *SafeSlice) Get(index int) (int, bool) {
        s.mu.RLock() // Blokkeert voor lezen
        defer s.mu.RUnlock()
        if index < 0 || index >= len(s.items) {
            return 0, false
        }
        return s.items[index], true
    }
    
  • Kanalen (Channels): Overdracht van eigendom van gegevens via kanalen. Eén goroutine schrijft gegevens, een andere leest ze uit het kanaal. Dit is geschikt voor scenario's met stroomverwerking van gegevens.

    // Voorbeeld van gebruik van kanaal voor het overdragen van elementen
    // in een scenario waar één goroutine produceert en een andere consumeert
    
  • sync.Map: Geschikt als de slice wordt gebruikt als basis voor het weergeven van sleutels op waarden of in scenario's waar de volgorde niet belangrijk is, maar gelijktijdige veiligheid cruciaal is.

Conclusie: Het gebruik van slices in gelijktijdige scenario's vereist expliciet beheer van synchronisatie om data races te voorkomen. Eenvoudige toegang tot een slice vanuit meerdere goroutines tegelijk is niet veilig.