Sobes.tech
Middle+

Quão seguro é usar slices no contexto de execução concorrente (Concurrency)?

sobes.tech IA

Resposta da IA

O uso de slices não é seguro no contexto de acesso concorrente sem mecanismos adicionais de sincronização. O acesso múltiplo simultâneo para leitura e escrita ou dois acessos simultâneos para escrita na mesma área de memória (que é o array base do slice) leva a condições de corrida (data race).

Razões para ser inseguro:

  1. Alteração do array base: Um slice é uma visualização do array base. Operações de adição de elementos (append), especialmente se excederem a capacidade atual, podem levar a uma realocação e cópia de dados para um novo array base. Se, nesse momento, outra goroutine tentar ler ou escrever no array base antigo ou novo, ocorre uma condição de corrida.
  2. Alteração no cabeçalho do slice: O cabeçalho do slice (uma estrutura que contém um ponteiro para o array base, o comprimento e a capacidade) também pode ser alterado durante operações como append. Leitura e escrita simultâneas nesse cabeçalho sem bloqueios levam a um comportamento indefinido.

Mecanismos para garantir a segurança:

  • sync.Mutex / sync.RWMutex: Bloqueia o acesso ao slice antes de cada operação de leitura ou escrita. sync.RWMutex é preferido em cenários com leituras frequentes e escritas raras.

    import "sync"
    
    type SafeSlice struct {
        mu    sync.RWMutex
        items []int
    }
    
    func (s *SafeSlice) Append(item int) {
        s.mu.Lock() // Bloqueia para escrita
        defer s.mu.Unlock()
        s.items = append(s.items, item)
    }
    
    func (s *SafeSlice) Get(index int) (int, bool) {
        s.mu.RLock() // Bloqueia para leitura
        defer s.mu.RUnlock()
        if index < 0 || index >= len(s.items) {
            return 0, false
        }
        return s.items[index], true
    }
    
  • Canais (Channels): Transferência de propriedade dos dados através de canais. Uma goroutine escreve dados, outra os lê do canal. Isso é adequado para cenários de processamento em fluxo de dados.

    // Exemplo de uso de canal para transmitir elementos
    // em um cenário onde uma goroutine produz e outra consome
    
  • sync.Map: Adequado se o slice é usado como base para mapear chaves para valores ou em cenários onde a ordem não importa, mas a segurança concorrente é crítica.

Conclusão: O uso de slices em cenários concorrentes requer gerenciamento explícito de sincronização para evitar condições de corrida. O acesso simples a um slice de várias goroutines simultaneamente não é seguro.