Cuando se trabaja en programación paralela o multisubproceso, una palabra que sale a colación cada dos por tres es "deadlock" (o un "interbloqueo" en castellano). Muchos porgramadores principiantes tienen dudas sobre qué son y cómo se pueden producir, por lo que, a raíz de la pregunta de un alumno de mi curso de fundamentos de C# y .NET, me he decidido a escribir algo que lo explique de manera simple pero efectiva.

Un deadlock, es una situación difícil de reproducir, y se dará sólo en algunas circunstancias muy concretas, por eso son situaciones muy difíciles de prever y de depurar.

Un ejemplo clásico de interbloqueo es el de una cuenta bancaria.

Consideremos el caso de una clase para transferir dinero entre cuentas bancarias, algo así:

class Cuenta 
{ 
   double saldo;

   void Retirar(double cantidad) 
   { 
      saldo -= cantidad; 
   }

   void Ingresar(double cantidad) 
   { 
      saldo += cantidad; 
   }

   void Transferir(Cuenta cuentaOrigen, Cuenta cuentaDestino, double cantidad) 
   { 
      lock(cuentaOrigen) 
      { 
         lock(cuentaDestino) 
         { 
            cuentaOrigen.Retirar(cantidad); 
            cuentaDestino.Ingresar(cantidad); 
         } 
      } 
   } 
}

Este código no parece tener problema alguno, y parece bastante lógico.

DeadlockEn la operación de transferencia bloqueas el acceso a las clases que representan las cuentas de origen y destino para evitar inconsistencias en sus saldos, ya que si no lo bloquearas y otro hilo retira o ingresa dinero mientras se está efectuando otra transferencia podrías tener inconsistencias en los saldos. De esta forma te aseguras de que la operación de transferencia es consistente. Hasta aquí todo controlado...

Pero ¿qué pasa si de repente dos hilos intentan acceder exactamente al mismo tiempo a hacer una transferencia entre las mismas dos cuentas, pero en sentidos contrarios?

Por ejemplo:

Transferir(cuentaA, CuentaB, 1000);

y

Transferir(cuentaB, CuentaA, 500);

Lo que ocurriría es que la primera transferencia bloquearía la cuentaA y la segunda la cuentaB (es la primera línea de código). Al ir a bloquear la segunda cuenta, la primera operación se encontraría con un bloqueo en la cuentaB creado por la otra transferencia, y viceversa. El resultado es que ambas operaciones quedarían bloqueadas indefinidamente porque una estaría esperando debido al bloqueo de la otra: ¡deadlock!

Lo malo de esta situación es que se produce solamente en un caso muy concreto como el que describo, por lo que podríamos hacer nuestro programa, tenerlo en producción y que pasaran meses hasta que surgiera el problema. Y una vez que hubiera surgido no tendríamos forma de saber exactamente qué ha pasado ni ninguna pista de cómo reproducirlo. Por eso los problemas de multi-subproceso son tan complejos.

¡Espero que esto lo aclare un poco!

¿Este post te ha ayudado?, ¿has aprendido algo nuevo? Entonces, me niego a creer que no puedas donar ni 1€ a los que necesitan algo tan básico como comer. Por cada euro que pongas tú, yo pondré otro.
¡No pases de ellos! También es tu responsabilidad.
Vamos a ayudarlos juntos