Sobes.tech
Middle

O que é um mutex, que tipos de mutex existem e como usá-los?

sobes.tech IA

Resposta da IA

O mutex (do inglês MUTual EXclusion) é uma primitiva de sincronização usada para garantir acesso exclusivo a um recurso compartilhado entre goroutines concorrentes. Ele garante que, em qualquer momento, apenas uma goroutine possa possuir o mutex e, consequentemente, acessar os dados protegidos.

No Go, o pacote sync fornece dois principais tipos de mutexes:

  • sync.Mutex: Mutex padrão. Fornece os métodos Lock() e Unlock(). Se o mutex já estiver bloqueado, a chamada a Lock() bloqueia a goroutine atual até que seja liberado. Unlock() desbloqueia o mutex.

    var mu sync.Mutex
    var counter int
    
    func increment() {
        mu.Lock() // Bloqueia o acesso ao counter
        counter++
        mu.Unlock() // Desbloqueia o acesso
    }
    
  • sync.RWMutex: Mutex para leitura/escrita. Permite que múltiplas goroutines leiam simultaneamente o recurso protegido, mas apenas uma escreva. Fornece os métodos Lock(), Unlock() (para escrita), RLock() e RUnlock() (para leitura).

    var rwMu sync.RWMutex
    var data []int
    
    func readData() {
        rwMu.RLock() // Bloqueia para leitura
        // Leitura de data
        fmt.Println(data)
        rwMu.RUnlock() // Desbloqueia a leitura
    }
    
    func writeData(val int) {
        rwMu.Lock() // Bloqueia para escrita
        // Escrita em data
        data = append(data, val)
        rwMu.Unlock() // Desbloqueia a escrita
    }
    

O uso de mutexes é necessário para prevenir condições de corrida (race conditions), quando várias goroutines tentam modificar os mesmos dados simultaneamente, levando a resultados imprevisíveis.

Regras de uso:

  1. Chamar Lock() antes de acessar o recurso protegido.
  2. Chamar Unlock() após terminar o acesso. Recomenda-se usar defer mu.Unlock() para garantir a liberação mesmo em caso de pânico.
  3. Não tentar bloquear um mutex já bloqueado pela mesma goroutine (pode levar a deadlock).
  4. Não tentar desbloquear um mutex não bloqueado.
  5. Ao usar RWMutex, usar RLock/RUnlock para operações de leitura e Lock/Unlock para operações de escrita.
  6. Os mutexes não devem ser copiados após o primeiro uso. Transmita-os por referência ou ponteiro.

Exemplo de uso de defer:

func safeIncrement() {
    mu.Lock()
    defer mu.Unlock() // Garante a liberação
    counter++
}