---
title: "deadlock — all goroutines are asleep em Go"
url: "https://golang.com.br/erros/deadlock-goroutines/"
markdown_url: "https://golang.com.br/erros/deadlock-goroutines.MD"
description: "Resolva o erro 'all goroutines are asleep - deadlock!' em Go. Aprenda a identificar causas de deadlock com channels, mutexes e WaitGroups."
date: "2026-05-12"
author: "Golang Brasil"
---

# deadlock — all goroutines are asleep em Go

Resolva o erro 'all goroutines are asleep - deadlock!' em Go. Aprenda a identificar causas de deadlock com channels, mutexes e WaitGroups.


# deadlock — all goroutines are asleep em Go

O erro **"fatal error: all goroutines are asleep - deadlock!"** é um panic do runtime do Go que acontece quando **todas** as goroutines do programa estão bloqueadas esperando por algo que nunca vai acontecer. O runtime detecta que nenhuma goroutine pode prosseguir e encerra o programa.

Este é um dos erros mais comuns ao trabalhar com [concorrência em Go](/aprenda/concorrencia-go/), especialmente com channels e mutexes.

---

## A Mensagem de Erro

```
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /app/main.go:8 +0x34
exit status 2
```

---

## Causas Comuns

### 1. Channel Sem Buffer — Leitura sem Goroutine para Escrever

Um channel sem buffer bloqueia até que haja alguém do outro lado:

```go
package main

func main() {
    ch := make(chan int) // Channel sem buffer

    // DEADLOCK: main espera para sempre — ninguém envia
    valor := <-ch
    _ = valor
}
```

### 2. Channel Sem Buffer — Escrita sem Goroutine para Ler

O inverso também causa deadlock:

```go
package main

func main() {
    ch := make(chan int)

    // DEADLOCK: main bloqueia tentando enviar
    // (ninguém está lendo do outro lado)
    ch <- 42
}
```

### 3. WaitGroup Mal Configurado

Esquecer de chamar `Done()` ou chamar `Add()` incorretamente:

```go
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    wg.Add(2) // Espera 2 Done()

    go func() {
        defer wg.Done()
        fmt.Println("goroutine 1")
    }()

    // Esqueceu a segunda goroutine!
    // Só 1 Done() será chamado, mas Add(2) espera 2

    // DEADLOCK: wg.Wait() espera para sempre
    wg.Wait()
}
```

### 4. Mutex Reentrante (Recursive Lock)

Go não suporta mutex reentrante. Tentar bloquear um mutex já bloqueado pela mesma goroutine causa deadlock:

```go
package main

import (
    "fmt"
    "sync"
)

var mu sync.Mutex

func funcA() {
    mu.Lock()
    defer mu.Unlock()
    fmt.Println("funcA")
    funcB() // Chama funcB que também tenta bloquear mu
}

func funcB() {
    mu.Lock() // DEADLOCK: mu já está bloqueado por funcA na mesma goroutine
    defer mu.Unlock()
    fmt.Println("funcB")
}

func main() {
    funcA()
}
```

### 5. Channels Cruzados

Duas goroutines esperando uma pela outra:

```go
package main

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        valor := <-ch1 // Espera ch1...
        ch2 <- valor   // ...depois envia em ch2
    }()

    go func() {
        valor := <-ch2 // Espera ch2...
        ch1 <- valor   // ...depois envia em ch1
    }()

    // DEADLOCK: goroutine 1 espera ch1, goroutine 2 espera ch2
    // Ninguém envia primeiro!
    select {}
}
```

---

## Como Resolver

### Solução 1: Usar Goroutine para Operações de Channel

Sempre tenha um produtor e consumidor em goroutines separadas:

```go
package main

import "fmt"

func main() {
    ch := make(chan int)

    // Goroutine produtora
    go func() {
        ch <- 42
    }()

    // Main consome
    valor := <-ch
    fmt.Println(valor) // 42
}
```

### Solução 2: Channels com Buffer

Channels com buffer não bloqueiam enquanto houver espaço:

```go
package main

import "fmt"

func main() {
    ch := make(chan int, 1) // Buffer de 1

    ch <- 42          // Não bloqueia (buffer tem espaço)
    valor := <-ch     // Lê do buffer
    fmt.Println(valor) // 42
}
```

### Solução 3: Fechar Channels Quando Terminar

Use `close()` para sinalizar que não há mais dados:

```go
package main

import "fmt"

func gerarNumeros(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch) // Sinaliza fim
}

func main() {
    ch := make(chan int)

    go gerarNumeros(ch)

    // range termina quando o channel é fechado
    for num := range ch {
        fmt.Println(num)
    }
}
```

### Solução 4: Corrigir WaitGroup

Garanta que `Add` e `Done` estejam balanceados:

```go
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    tarefas := []string{"A", "B", "C"}

    for _, tarefa := range tarefas {
        wg.Add(1)
        go func(t string) {
            defer wg.Done()
            fmt.Println("Processando:", t)
        }(tarefa)
    }

    wg.Wait() // Funciona: cada goroutine chama Done()
    fmt.Println("Todas as tarefas concluídas")
}
```

### Solução 5: Usar select com Timeout

Evite bloqueios infinitos com `select` e `time.After`:

```go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)

    go func() {
        time.Sleep(5 * time.Second) // Simula operação lenta
        ch <- 42
    }()

    select {
    case valor := <-ch:
        fmt.Println("Recebido:", valor)
    case <-time.After(2 * time.Second):
        fmt.Println("Timeout: operação demorou demais")
    }
}
```

Para timeouts mais sofisticados, use o pacote [context](/aprenda/concorrencia-go/).

---

## Quando o Runtime NÃO Detecta Deadlock

O runtime do Go só detecta deadlock quando **todas** as goroutines estão bloqueadas. Se uma goroutine continua executando (mesmo que não contribua), o deadlock parcial passa despercebido:

```go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)

    // Esta goroutine nunca envia em ch
    go func() {
        for {
            time.Sleep(time.Second)
            fmt.Println("estou viva!")
        }
    }()

    // Bloqueia para sempre, mas o runtime NÃO detecta deadlock
    // porque a outra goroutine está ativa
    <-ch
}
```

Para detectar esses casos, use [profiling](/tutoriais/go-performance-profiling/) e monitore goroutines bloqueadas com `pprof`.

---

## Dicas para Evitar Deadlocks

1. **Sempre feche channels** — quando o produtor terminar, use `close(ch)`. Leia sobre [padrões de concorrência](/tutoriais/go-concurrency-patterns/).

2. **Use `select` com default ou timeout** — nunca bloqueie indefinidamente sem escape.

3. **Evite mutex reentrante** — reestruture o código para que a mesma goroutine não tente bloquear o mesmo mutex duas vezes.

4. **Use o race detector** — `go run -race .` detecta condições de corrida que podem levar a deadlocks. Veja [data race detected](/erros/race-condition-data-race/).

5. **Prefira channels a mutexes** — channels comunicam intenção mais claramente. Consulte [concorrência em Go](/aprenda/concorrencia-go/).

6. **Teste com `-count` e `-race`** — testes de concorrência devem rodar múltiplas vezes para expor race conditions: `go test -race -count=100`.

Para comparação, <a href="https://rustlang.com.br/blog/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a> previne deadlocks e data races em tempo de compilação através do sistema de ownership e borrow checker — uma abordagem fundamentalmente diferente da detecção em runtime do Go.

---

## Erros Relacionados

- [race condition / data race](/erros/race-condition-data-race/) — acesso concorrente sem sincronização
- [nil pointer dereference](/erros/nil-pointer-dereference/) — outro panic comum em runtime
- [Concorrência em Go](/aprenda/concorrencia-go/) — guia completo de goroutines e channels
- [Padrões de Concorrência](/tutoriais/go-concurrency-patterns/) — patterns como fan-out, fan-in, pipeline
- [Testes em Go](/aprenda/testes-go/) — como testar código concorrente
