Última atualização:2026-10-12
Depuração de Deadlocks em C++¶
Como investigar deadlocks em programação concorrente com C++?¶
Recentemente escrevi no Apache Arrow um operador com suporte a paralelismo: nested loop join. Como envolve paralelismo, aparece o famoso problema de deadlock. Se você se deparar com um deadlock, como depurar e localizá-lo?
Esse é o objetivo deste artigo: ajudá-lo a dominar rapidamente a detecção e a depuração de deadlocks em programação concorrente. Conteúdo direto ao ponto; vamos ao texto.
1.Introdução¶
Para explicar melhor o deadlock, introduzimos com um programa.
std::mutex gMutex;
int t2() {
std::lock_guard<std::mutex> m(gMutex);
return 0;
}
int t1() {
std::lock_guard<std::mutex> l(gMutex);
return t2();
}
Ao ver este programa, todos percebem o problema: deadlock! O problema é que tanto t1() quanto t2() adquirem o mutex global gMutex, mas t1(), após adquiri-lo, chama t2(), que por sua vez tenta adquirir gMutex novamente.
O lock de t1 já foi adquirido e ainda não foi liberado, e t2 tenta adquiri-lo novamente; ambos ficam esperando um pelo outro e entram em loop infinito. Em projetos reais o código não é tão simples quanto este — é muito complexo. Por exemplo, o código que escrevi no Apache Arrow é assim:
Status OnBuildSideFinished(size_t thread_index) {
std::lock_guard<std::mutex> guard(probe_side_mutex_);
// do something
accumulate_build_ready_ = true;
return scheduler_->StartTaskGroup(thread_index,task_group_probe_,queued_batches_to_probe_.batch_count());
}
Veja que este código é bem mais complexo que o cenário acima, com pelo menos 5 níveis de pilha aninhados — e ainda nem colei o restante. Mas, na essência, ambos seguem o mesmo modelo de deadlock.
2.Depuração¶
Apresentado o modelo de deadlock, como localizar o problema quando ele acontece?
Há duas abordagens. A primeira é executar o programa diretamente e depois anexar o gdb.
Por exemplo:
Depois de encontrar o número do processo:
Neste momento podemos obter as threads que estão esperando.
(gdb) info threads
Id Target Id Frame
* 1 Thread 0x7ffff7fe2740 (LWP 32301) "a.out" 0x00007ffff7bc8017 in pthread_join () from /lib64/libpthread.so.0
2 Thread 0x7ffff6fd0700 (LWP 32305) "a.out" 0x00007ffff7bcd54d in __lll_lock_wait () from /lib64/libpthread.so.0
Em seguida, olhe a pilha de __lll_lock_wait; aqui examinei a thread 2 e, ao ver a pilha, obtive os números de linha de t1 e t2, localizando diretamente onde está o problema — muito intuitivo!
Além dessa forma, também é possível iniciar o programa direto no gdb; ele travará, e após matar com ctrl + c você obtém o mesmo conteúdo do caso acima.
Por exemplo:
(gdb) r
Starting program: /home/light/a.out
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Hello World!
[New Thread 0x7ffff6fd0700 (LWP 32305)]
^C
Thread 1 "a.out" received signal SIGINT, Interrupt.
0x00007ffff7bc8017 in pthread_join () from /lib64/libpthread.so.0
Missing separate debuginfos, use: debuginfo-install libgcc-4.8.5-44.el7.x86_64 libstdc++-4.8.5-44.el7.x86_64
(gdb) info threads
Id Target Id Frame
* 1 Thread 0x7ffff7fe2740 (LWP 32301) "a.out" 0x00007ffff7bc8017 in pthread_join () from /lib64/libpthread.so.0
2 Thread 0x7ffff6fd0700 (LWP 32305) "a.out" 0x00007ffff7bcd54d in __lll_lock_wait () from /lib64/libpthread.so.0
Bom, é isso para esta seção. Se gostou, compartilhe este artigo hardcore! Ao mesmo tempo, recomendo meu projeto de thread pool: expliquei o projeto em vídeo e incluí implantação com um clique via docker (pretendo gravar esse episódio em breve); se tiver interesse, é só me procurar.
Tópicos relacionados¶
- Guia de C++ Concurrency in Action | C++那些事
- Vetorização SIMD em C++ | C++那些事
- STL Paralela do C++17 | C++那些事
- False Sharing e Linhas de Cache em C++ | C++那些事
- Depuração All-Stop multi-thread com GDB | C++那些事
- Detecção de Erros de Concorrência com Helgrind | C++那些事
- Princípio do event loop do libev | C++那些事
- Implementação interna de std::thread em C++ | C++那些事
- C++ std::thread e jthread | C++那些事
- Introdução a multi-thread em C++ | C++那些事