Ctrl+C em Go: interceptando SIGINT e limpeza com «defer» sob o capô

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

É possível interceptar Ctrl+C em Go e executar uma limpeza, como o defer? Análise sob o capô

\n\n

Você já iniciou um servidor em Go, pressionou Ctrl+C e pensou: "E se, antes de sair, eu pudesse fechar cuidadosamente as conexões com o banco de dados, salvar o cache ou simplesmente dizer 'Tchau!'?" A pergunta é tão popular no Stack Overflow (milhares de visualizações!) que decidimos analisá-la detalhadamente. Hoje, não vamos apenas dar uma receita pronta, mas vamos olhar sob o capô — como o Go lida com sinais, o que é signal.Notify e por que o defer não está totalmente envolvido aqui.

\n\n

Prontos? Vamos lá!

\n\n

Problema: Ctrl+C mata o programa instantaneamente

\n\n

Por padrão, quando você pressiona Ctrl+C no terminal, o SO envia o sinal SIGINT ao processo. O programa Go, se nada for feito, termina abruptamente — todas as goroutines são interrompidas, as funções defer não são executadas. É como puxar o plugue da tomada: o computador desliga, mas os dados podem ser perdidos.

\n\n

Exemplo clássico: você escreve um servidor web, abre uma conexão com o PostgreSQL e, de repente — Ctrl+C. Se o sinal não for tratado, a conexão ficará pendente e o pool de conexões não será liberado. Após algumas reinicializações assim, o banco de dados dirá: "Desculpe, não aguento mais".

\n\n
package main\n\nimport (\n    "fmt"\n    "os"\n    "os/signal"\n    "syscall"\n)\n\nfunc main() {\n    // ... seu código\n    fmt.Println("Pressione Ctrl+C para finalizar")\n    // Aguarda o sinal\n    sig := make(chan os.Signal, 1)\n    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)\n    <-sig\n    fmt.Println("Sinal recebido, finalizando...")\n}
\n\n

Este código já captura o sinal, mas e se precisarmos executar uma limpeza complexa? É aí que a mágica começa.

\n\n

Como funciona sob o capô: signal.Notify e canais

\n\n

A função signal.Notify registra seu canal para receber sinais específicos. Quando o SO envia um sinal, o runtime do Go o coloca no canal. Se o canal for bufferizado (e fizemos um buffer de 1), o sinal não será perdido, mesmo que a goroutine receptora esteja ocupada.

\n\n

Uma nuance importante: signal.Notify intercepta o sinal, mas não bloqueia sua propagação. Se você não processar o sinal no canal, o programa ainda será encerrado após a execução dos defer (se houverem no main). Mas se você quiser executar uma limpeza — é necessário aguardar sua conclusão.

\n\n

Esquema de funcionamento:

\n
    \n
  • SO → envia SIGINT ao processo
  • \n
  • Runtime Go → coloca o sinal no canal registrado via signal.Notify
  • \n
  • Sua goroutine → lê do canal e inicia a limpeza
  • \n
  • Após a limpeza → você chama os.Exit(0) ou simplesmente sai do main
  • \n
\n\n

Mas como fazer a limpeza "no estilo defer"? Afinal, o defer é executado ao retornar da função, não ao receber um sinal. Resposta: usamos defer + bloqueio do main até receber o sinal.

\n\n

Receita: limpeza "tipo defer" ao pressionar Ctrl+C

\n\n

A ideia é simples: escrevemos funções de limpeza que devem ser executadas ao finalizar e as "penduramos" no defer. Em seguida, bloqueamos o main até receber o sinal. Quando o sinal chega, o main é finalizado e todos os defer são acionados. Elegante, não?

\n\n
package main\n\nimport (\n    "fmt"\n    "os"\n    "os/signal"\n    "syscall"\n    "time"\n)\n\nfunc cleanup() {\n    fmt.Println("Limpeza: fechando conexões...")\n    time.Sleep(2 * time.Second) // simulação de trabalho\n    fmt.Println("Limpeza concluída")\n}\n\nfunc main() {\n    // defer será executado em qualquer finalização do main\n    defer cleanup()\n\n    // Canal para o sinal\n    sig := make(chan os.Signal, 1)\n    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)\n\n    fmt.Println("Servidor iniciado. Pressione Ctrl+C para parar.")\n    fmt.Println("Executando limpeza...")\n\n    //                

Blogs

Book Recommendations