В Go паника (panic) используется для сигнализации о фатальной ошибке в программе. После вызова panic выполнение начинается с unwind стека и вызова всех deferred-функций. Если в стеке есть функция с вызовом recover(), то она может перехватить и обработать панику — но recover работает только внутри своей горутины и только при вызове из defer.
Рекомендованный паттерн для безопасного восстановления:
func safe(fn func()) { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() fn() } go safe(func() { panic("fail!") // не приводит к остановке программы })
Важно помнить, что:
panic завершает выполнение текущей горутины вплоть до первого recoverМожет ли recover перехватить pаникy, произошедшую в другой горутине?
Ответ: Нет, recover работает только внутри горутины, в которой произошла паника, и только если вызван в отложенной (defer) функции. Если паника возникла в одной горутине, а recover вызывается в другой — перехвата не произойдет, программа аварийно завершит выполнение.
Пример:
func main() { go func() { panic("inside goroutine") }() time.Sleep(time.Second) recover() // Не сработает! Panic завершит программу }
История
Разработчик реализовал обработку ошибок с помощью pattern defer recover() только в основной функции. Когда паника произошла внутри worker'а в отдельной горутине, вся программа аварийно завершилась — ошибка не была перехвачена глобальным recover.
История
В проекте использовали defer/recover для graceful shutdown веб-сервера, считая, что этого достаточно. Оказалось, что паника в одной из горутин логики завершала весь процесс, потому что обработка recover стояла не в нужном месте — пришлось рефакторить весь воркер-пул.
История
В потоке обработки сообщений возникла паника из-за неправильной работы user-функции. Разработчик ожидал, что её "поймает" recover верхнего уровня, но не осознал, что recover работает ТОЛЬКО из defer. В результате сервис крашился нерегулярно, когда в очереди появлялось некорректное сообщение.