Saltar a contenido

Última actualización:2026-10-12

Localización de interbloqueos en C++

¿Cómo investigar un interbloqueo en programación concurrente con C++?

Últimamente estoy escribiendo en Apache arrow un operador que admite paralelismo: nested loop join; y al tratarse de algo paralelo, aparece el típico problema del interbloqueo. Si te topas con un interbloqueo, ¿cómo depurarlo y localizarlo?

Ese es el objetivo de este artículo: ayudarte a dominar rápidamente, en programación concurrente, la detección y la depuración de interbloqueos. Contenido muy denso; bueno, entremos en materia.

1. Introducción

Para explicar mejor los interbloqueos, lo introducimos con un 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();
}

Seguro que al ver este programa todo el mundo piensa que tiene un problema: ¡se produce un interbloqueo! El problema está en que tanto la función t1() como la función t2() adquieren el mutex global gMutex, pero la función t1(), tras adquirirlo, llama a la función t2(), que a su vez intenta adquirir gMutex otra vez.

El bloqueo de t1 ya está tomado y aún no se ha liberado, y t2 intenta tomarlo de nuevo: ambos se esperan mutuamente a que el otro libere y entran en un bucle infinito. En un proyecto real el código no es tan simple como este, sino muy complejo; por ejemplo, el código que escribí en Apache arrow es así:

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()); 
}

Como ves, este código es mucho más complejo que el escenario anterior: anida al menos 5 niveles de pila y aún queda código sin mostrar, pero en esencia ambos responden al mismo modelo de interbloqueo.

2. Depuración

Explicado el modelo de interbloqueo, ¿cómo localizarlo si nos encontramos con este problema?

Aquí se pueden usar dos métodos. El primero: ejecutar el programa directamente y luego entrar con gdb.

Por ejemplo:

./a.out

Después de encontrar el número de proceso:

gdb -p xxx

En este momento podemos ver los hilos que están 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

Luego vamos a mirar la pila de __lll_lock_wait; por ejemplo, aquí inspeccioné el hilo 2 y, consultando la pila, obtuve los números de línea de t1 y t2, con lo que se localiza directamente dónde está el problema. ¡Muy intuitivo!

Aparte de este método, también se puede entrar directamente con gdb y ejecutar el programa; se quedará colgado y, tras matarlo con ctrl + c, se obtiene lo mismo que arriba.

Por ejemplo:

(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

Bien, esto es todo por esta sección; si te ha interesado, ¡reenvía este artículo tan hardcore! Al mismo tiempo, recomiendo mi proyecto de pool de hilos, que explico personalmente en vídeo y que además incluye un despliegue en un paso con docker (pienso grabar más adelante el vídeo de esta entrega); si te interesa, simplemente contáctame.

Temas relacionados

Comentarios