all goroutines are asleep — deadlock
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()Go остановил программу, потому что все её горутины чего-то ждут — значения из канала, места в канале, Wait, мьютекса (замка для общих данных), — и ни одна не может продолжить. В квадратных скобках — чего ждёт горутина: chan send — отправки в канал, chan receive — получения из канала. Ниже указаны файл и строка: откройте эту строку кода и найдите свой случай среди разделов ниже.
Отправка в канал без получателя
ch := make(chan int)
ch <- 1 // ждёт получателя вечно
fmt.Println(<-ch)Канал без буфера передаёт значение только из рук в руки: отправка ждёт, пока кто-то в другой горутине начнёт получать. Здесь получатель — та же горутина, строкой ниже, и до него дело не дойдёт. Решения: отправлять из отдельной горутины или дать каналу буфер.
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
fmt.Println(<-ch)
buf := make(chan int, 1)
buf <- 2
fmt.Println(<-buf)
}1
2range по каналу, который не закрыли
for v := range ch {
fmt.Println(v)
}Цикл заканчивается, только когда канал закрыт. Если отправитель отправил все значения, но не вызвал close(ch), range ждёт следующего значения вечно — это chan receive в сообщении. Закрывайте канал там, где отправка закончилась: close(ch) после цикла отправки или defer close(ch) в начале горутины-отправителя.
WaitGroup: Add и Done не сошлись
wg.Wait() ждёт, пока число Done не сравняется с суммой Add. Если Add(1) вызвано больше раз, чем запущено горутин, или горутина выходит, не вызвав Done, Wait не дождётся. Правила:
wg.Add(1)— прямо передgo func(), по одному на горутину;defer wg.Done()— первой строкой в горутине, чтобыDoneвызвался при любом выходе.
WaitGroup передана по значению
func worker(wg sync.WaitGroup) { // ошибка: копия
defer wg.Done()
}Функция получила копию WaitGroup и уменьшает счётчик у копии, а main ждёт оригинал. Передавайте указатель: func worker(wg *sync.WaitGroup) и вызов worker(&wg). То же относится к sync.Mutex. Такую ошибку находит go vet: сообщение passes lock by value.
Горутина ждёт сама себя
Повторный mu.Lock() в той же горутине без Unlock между ними блокирует её навсегда: мьютекс в Go не запоминает, кто его взял. Если других работающих горутин нет — та же ошибка deadlock.
Когда Go не скажет «deadlock»
Сообщение появляется, только когда заблокированы все горутины. Если хоть одна ещё работает — например, HTTP-сервер ждёт подключений, — зависшие горутины просто молча висят. Тогда помогает снимок всех горутин: в Linux и macOS отправьте программе сигнал SIGQUIT (Ctrl+\ в терминале), и она напечатает стек каждой горутины перед выходом.
Подробно о каналах и WaitGroup — в уроке Горутины и каналы.