---
title: "data race detected em Go"
url: "https://golang.com.br/erros/race-condition-data-race/"
markdown_url: "https://golang.com.br/erros/race-condition-data-race.MD"
description: "Resolva 'data race detected' em Go. Aprenda a usar o race detector, identificar condições de corrida e proteger dados com mutex e channels."
date: "2026-05-12"
author: "Golang Brasil"
---

# data race detected em Go

Resolva 'data race detected' em Go. Aprenda a usar o race detector, identificar condições de corrida e proteger dados com mutex e channels.


# data race detected em Go

Uma **data race** (condição de corrida de dados) ocorre quando duas ou mais goroutines acessam a mesma variável de memória simultaneamente, e pelo menos uma delas está escrevendo. O Go possui um **race detector** integrado que detecta essas condições em tempo de execução, emitindo o aviso **"WARNING: DATA RACE"** e detalhando exatamente onde o acesso concorrente acontece.

Data races são bugs sérios: podem causar corrupção de dados, panics inesperados e comportamentos não determinísticos que são extremamente difíceis de reproduzir e debugar.

---

## A Mensagem de Erro

```
==================
WARNING: DATA RACE
Read at 0x00c0000a4000 by goroutine 7:
  main.main.func1()
      /app/main.go:15 +0x3e

Previous write at 0x00c0000a4000 by goroutine 8:
  main.main.func2()
      /app/main.go:21 +0x4e

Goroutine 7 (running) created at:
  main.main()
      /app/main.go:13 +0x8e

Goroutine 8 (running) created at:
  main.main()
      /app/main.go:19 +0xb8
==================
Found 1 data race(s)
exit status 66
```

---

## Como Usar o Race Detector

O race detector é ativado com a flag `-race`:

```bash
# Executar com race detector
go run -race .

# Testar com race detector
go test -race ./...

# Build com race detector (para ambientes de staging)
go build -race -o app .
```

O race detector adiciona overhead de CPU (~2-10x mais lento) e memória (~5-10x mais memória), então use em desenvolvimento e [testes](/aprenda/testes-go/), não em produção.

---

## Causas Comuns

### 1. Variável Compartilhada Sem Proteção

O caso clássico — múltiplas goroutines acessam a mesma variável:

```go
package main

import (
    "fmt"
    "sync"
)

func main() {
    contador := 0
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            // DATA RACE: leitura e escrita concorrentes
            contador++
        }()
    }

    wg.Wait()
    // Resultado imprevisível (pode ser 980, 995, 1000...)
    fmt.Println(contador)
}
```

### 2. Map Concorrente

Maps em Go não são thread-safe:

```go
package main

import "sync"

func main() {
    m := make(map[string]int)
    var wg sync.WaitGroup

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            // DATA RACE: escrita concorrente em map
            // Pode causar: "fatal error: concurrent map writes"
            m[fmt.Sprintf("key-%d", n)] = n
        }(i)
    }

    wg.Wait()
}
```

### 3. Slice Compartilhado

Slices com append concorrente:

```go
package main

import (
    "fmt"
    "sync"
)

func main() {
    var resultados []int
    var wg sync.WaitGroup

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            // DATA RACE: append concorrente modifica o slice header
            resultados = append(resultados, n)
        }(i)
    }

    wg.Wait()
    fmt.Println(len(resultados)) // Resultado imprevisível
}
```

### 4. Struct com Campos Acessados por Múltiplas Goroutines

```go
package main

import (
    "sync"
)

type Server struct {
    connections int
    running     bool
}

func main() {
    s := &Server{}
    var wg sync.WaitGroup

    // Goroutine 1: modifica connections
    wg.Add(1)
    go func() {
        defer wg.Done()
        s.connections++ // DATA RACE
    }()

    // Goroutine 2: lê connections
    wg.Add(1)
    go func() {
        defer wg.Done()
        _ = s.connections // DATA RACE
    }()

    wg.Wait()
}
```

---

## Como Resolver

### Solução 1: sync.Mutex

O mutex (mutual exclusion) garante que apenas uma goroutine acessa o recurso por vez:

```go
package main

import (
    "fmt"
    "sync"
)

func main() {
    contador := 0
    var mu sync.Mutex
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.Lock()
            contador++
            mu.Unlock()
        }()
    }

    wg.Wait()
    fmt.Println(contador) // Sempre 1000
}
```

Para leituras frequentes e escritas raras, use `sync.RWMutex`:

```go
var mu sync.RWMutex

// Leitura — múltiplas goroutines podem ler simultaneamente
mu.RLock()
valor := mapa["chave"]
mu.RUnlock()

// Escrita — exclusiva, bloqueia todas as leituras
mu.Lock()
mapa["chave"] = 42
mu.Unlock()
```

### Solução 2: sync/atomic

Para operações simples em inteiros e ponteiros, use operações atômicas:

```go
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    var contador atomic.Int64
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            contador.Add(1) // Operação atômica — sem data race
        }()
    }

    wg.Wait()
    fmt.Println(contador.Load()) // Sempre 1000
}
```

### Solução 3: sync.Map para Maps Concorrentes

Use `sync.Map` quando múltiplas goroutines precisam acessar um map:

```go
package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    var wg sync.WaitGroup

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            m.Store(fmt.Sprintf("key-%d", n), n)
        }(i)
    }

    wg.Wait()

    // Leitura segura
    m.Range(func(key, value any) bool {
        fmt.Printf("%s: %v\n", key, value)
        return true
    })
}
```

### Solução 4: Channels (Comunicação ao Invés de Compartilhamento)

O provérbio Go diz: *"Don't communicate by sharing memory; share memory by communicating."*

```go
package main

import "fmt"

func main() {
    resultados := make(chan int, 100)
    done := make(chan bool)

    // Produtor: goroutines enviam resultados pelo channel
    for i := 0; i < 100; i++ {
        go func(n int) {
            resultados <- n * 2
        }(i)
    }

    // Consumidor: uma única goroutine agrega
    go func() {
        var soma int
        for i := 0; i < 100; i++ {
            soma += <-resultados
        }
        fmt.Println("Soma:", soma)
        done <- true
    }()

    <-done
}
```

Veja mais sobre este padrão em [concorrência em Go](/aprenda/concorrencia-go/) e [padrões de concorrência](/tutoriais/go-concurrency-patterns/).

### Solução 5: Confinar Dados a Uma Goroutine

Às vezes a melhor solução é não compartilhar:

```go
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    resultados := make([]int, 100) // Pre-alocado

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(idx int) {
            defer wg.Done()
            // Cada goroutine acessa apenas SEU índice
            // Sem compartilhamento = sem data race
            resultados[idx] = idx * 2
        }(i)
    }

    wg.Wait()
    fmt.Println(resultados[:5]) // [0 2 4 6 8]
}
```

---

## Integrando o Race Detector no CI/CD

Adicione o race detector ao seu pipeline de [testes e CI/CD](/tutoriais/go-tdd-ci-cd/):

```yaml
# .github/workflows/test.yml
- name: Run tests with race detector
  run: go test -race -count=5 ./...
```

Usar `-count=5` executa cada teste 5 vezes, aumentando a chance de expor race conditions intermitentes.

---

## Dicas para Evitar Data Races

1. **Execute testes com `-race` sempre** — integre ao CI. Veja [TDD e CI/CD em Go](/tutoriais/go-tdd-ci-cd/).

2. **Prefira channels a mutexes** — channels deixam o fluxo de dados explícito. Leia sobre [concorrência em Go](/aprenda/concorrencia-go/).

3. **Use `sync/atomic` para contadores** — mais eficiente que mutex para operações simples.

4. **Imutabilidade** — passe cópias ao invés de ponteiros quando possível.

5. **Evite goroutines que compartilham estado** — confine dados quando possível. Consulte [padrões de concorrência](/tutoriais/go-concurrency-patterns/).

6. **Use [observabilidade](/tutoriais/go-observability/)** — monitore goroutines e contenção de locks em produção com [pprof e profiling](/tutoriais/go-performance-profiling/).

Curiosamente, <a href="https://rustlang.com.br/blog/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a> elimina data races completamente em tempo de compilação graças ao ownership — o compilador garante que dados mutáveis nunca são compartilhados entre threads sem sincronização explícita.

---

## Erros Relacionados

- [deadlock — all goroutines are asleep](/erros/deadlock-goroutines/) — quando sincronização excessiva bloqueia tudo
- [nil pointer dereference](/erros/nil-pointer-dereference/) — data races podem causar ponteiros nil inesperados
- [Concorrência em Go](/aprenda/concorrencia-go/) — fundamentos de goroutines e channels
- [Padrões de Concorrência](/tutoriais/go-concurrency-patterns/) — fan-out, fan-in, pipeline, worker pool
