Compartir a través de


Condiciones de carrera y interbloqueos

Visual Basic .NET o Visual Basic ofrecen la capacidad de usar subprocesos en aplicaciones de Visual Basic por primera vez. Los subprocesos presentan problemas de depuración, como condiciones de carrera y interbloqueos. En este artículo se exploran estos dos problemas.

Versión original del producto: Visual Basic, Visual Basic .NET
Número de KB original: 317723

Cuando se producen condiciones de carrera

Una condición de carrera se produce cuando dos subprocesos acceden a una variable compartida al mismo tiempo. El primer subproceso lee la variable y el segundo subproceso lee el mismo valor de la variable. A continuación, el primer subproceso y el segundo subproceso realizan sus operaciones en el valor y compiten por ver qué subproceso puede escribir el último valor en la variable compartida. Se conserva el valor del subproceso que escribe su valor por última vez, porque el subproceso escribe sobre el valor que escribió el subproceso anterior.

Detalles y ejemplos de una condición de carrera

A cada subproceso se le asigna un período de tiempo predefinido para ejecutarse en un procesador. Cuando expira la hora asignada para el subproceso, el contexto del subproceso se guarda hasta su siguiente turno en el procesador y el procesador comienza la ejecución del siguiente subproceso.

¿Cómo puede un comando de una línea producir una condición de carrera?

Examine el ejemplo siguiente para ver cómo se produce una condición de carrera. Hay dos subprocesos y ambos actualizan una variable compartida denominada total (que se representa como dword ptr ds:[031B49DCh] en el código de ensamblado).

  • Subproceso 1

    Total = Total + val1
    
  • Subproceso 2

    Total = Total - val2
    

Código de ensamblado (con números de línea) de la compilación del código de Visual Basic anterior:

  • Subproceso 1

    1. mov eax,dword ptr ds:[031B49DCh]
    2. add eax,edi
    3. jno 00000033
    4. xor ecx,ecx
    5. call 7611097F
    6. mov dword ptr ds:[031B49DCh],eax
    
  • Subproceso 2

    1. mov eax,dword ptr ds:[031B49DCh]
    2. sub eax,edi
    3. jno 00000033
    4. xor ecx,ecx
    5. call 76110BE7
    6. mov dword ptr ds:[031B49DCh],eax
    

Al examinar el código de ensamblado, puede ver cuántas operaciones realiza el procesador en el nivel inferior para ejecutar un cálculo de suma simple. Un subproceso puede ejecutar todo o parte de su código de ensamblado durante su tiempo en el procesador. Ahora vea cómo se produce una condición de carrera a partir de este código.

Total es 100, val1 es 50 y val2 es 15. El subproceso 1 tiene la oportunidad de ejecutarse, pero solo completa los pasos del 1 al 3. Esto significa que el subproceso 1 lee la variable y completa la adición. El subproceso 1 está esperando a escribir su nuevo valor de 150. Una vez detenido el subproceso 1 , el subproceso 2 se ejecuta completamente. Esto significa que ha escrito el valor que calculó (85) en la variable Total. Por último, El subproceso 1 recupera el control y finaliza la ejecución. Escribe su valor (150). Por lo tanto, cuando finaliza el subproceso 1 , el valor de Total es ahora 150 en lugar de 85.

Puede ver cómo podría ser un problema importante. Si se trata de un programa bancario, el cliente tendría dinero en su cuenta que no debería estar presente.

Este error es aleatorio, ya que es posible que el subproceso 1 complete su ejecución antes de que expire el procesador y, a continuación , subproceso 2 puede comenzar su ejecución. Si se producen estos eventos, el problema no se produce. La ejecución del subproceso no es determinista, por lo que no se puede controlar el tiempo ni el orden de ejecución. Tenga en cuenta también que los subprocesos pueden ejecutarse de forma diferente en tiempo de ejecución frente al modo de depuración. Además, puede ver que si ejecuta cada subproceso en serie, el error no se produce. Esta aleatoriedad hace que estos errores sean mucho más difíciles de rastrear y depurar.

Para evitar que se produzcan las condiciones de carrera, puede bloquear variables compartidas, de modo que solo un subproceso tenga acceso a la variable compartida. Haga esto con moderación, ya que si una variable está bloqueada en el subproceso 1 y el subproceso 2 también necesita la variable, la ejecución de Thread 2 se detiene mientras el subproceso 2 espera a que Thread 1 libere la variable. (Para obtener más información, consulte SyncLock la sección Referencias de este artículo).

Síntomas de una condición de raza

El síntoma más común de una condición de carrera es valores impredecibles de variables que se comparten entre varios subprocesos. Esto resulta de la imprevisibilidad del orden en que se ejecutan los subprocesos. En algún momento gana un subproceso y, en algún momento, el otro subproceso gana. En otros momentos, la ejecución funciona correctamente. Además, si cada subproceso se ejecuta por separado, el valor de la variable se comporta correctamente.

Cuando se producen interbloqueos

Un interbloqueo se produce cuando dos subprocesos bloquean una variable diferente al mismo tiempo y, a continuación, intentan bloquear la variable que el otro subproceso ya está bloqueado. Como resultado, cada subproceso deja de ejecutarse y espera a que el otro subproceso libere la variable. Dado que cada subproceso contiene la variable que el otro subproceso desea, no se produce nada y los subprocesos permanecen interbloqueados.

Detalles y ejemplos de interbloqueos

El código siguiente tiene dos objetos LeftVal y RightVal:

  • Subproceso 1

    SyncLock LeftVal
        SyncLock RightVal
            'Perform operations on LeftVal and RightVal that require read and write.
        End SyncLock
    End SyncLock
    
  • Subproceso 2

    SyncLock RightVal
        SyncLock LeftVal
            'Perform operations on RightVal and LeftVal that require read and write.
        End SyncLock
    End SyncLock
    

Un interbloqueo se produce cuando se permite bloquear LeftValel subproceso 1. El procesador detiene la ejecución del subproceso 1 y comienza la ejecución del subproceso 2. El subproceso RightVal 2 bloquea y, a continuación, intenta bloquear LeftVal. Como LeftVal está bloqueado, el subproceso LeftVal 2 se detiene y espera a que se libere. Dado que el subproceso 2 está detenido, se permite que el subproceso 1 continúe ejecutándose. El subproceso 1 intenta bloquear RightVal pero no puede, porque el subproceso 2 lo ha bloqueado. Como resultado, Thread 1 comienza a esperar hasta que RightVal esté disponible. Cada subproceso espera al otro subproceso, porque cada subproceso ha bloqueado la variable en la que el otro subproceso está esperando y ninguno de los subprocesos está desbloqueando la variable que contiene.

No siempre se produce un interbloqueo. Si Thread 1 ejecuta ambos bloqueos antes de que el procesador lo detenga, Thread 1 puede realizar sus operaciones y, a continuación, desbloquear la variable compartida. Después de que Thread 1 desbloquea la variable, Thread 2 puede continuar con su ejecución, según lo previsto.

Este error parece obvio cuando estos fragmentos de código se colocan en paralelo, pero, en la práctica, el código puede aparecer en módulos o áreas independientes del código. Se trata de un error difícil de rastrear porque, desde este mismo código, puede producirse la ejecución correcta y la ejecución incorrecta.

Síntomas de interbloqueos

Un síntoma común de interbloqueo es que el programa o grupo de subprocesos deja de responder. Esto también se conoce como un bloqueo. Al menos dos subprocesos están esperando una variable que el otro subproceso bloqueó. Los subprocesos no continúan, ya que ninguno de los subprocesos liberará su variable hasta que obtenga la otra variable. Todo el programa puede bloquearse si el programa está esperando a que uno o ambos subprocesos completen la ejecución.

¿Qué es un subproceso?

Los procesos se usan para separar las distintas aplicaciones que se ejecutan en un momento especificado en un único equipo. El sistema operativo no ejecuta procesos, pero sí subprocesos. Un subproceso es una unidad de ejecución. El sistema operativo asigna tiempo de procesador a un subproceso para la ejecución de las tareas del subproceso. Un único proceso puede contener varios subprocesos de ejecución. Cada subproceso mantiene sus propios controladores de excepciones, prioridades de programación y un conjunto de estructuras que usa el sistema operativo para guardar el contexto del subproceso si el subproceso no puede completar su ejecución durante el tiempo asignado al procesador. El contexto se mantiene hasta la próxima vez que el subproceso reciba la hora del procesador. El contexto incluye toda la información que requiere el subproceso para continuar sin problemas su ejecución. Esta información incluye el conjunto de registros de procesador del subproceso y la pila de llamadas dentro del espacio de direcciones del proceso host.

Referencias

Para obtener más información, busque la Ayuda de Visual Studio para ver las siguientes palabras clave:

  • SyncLock. Permite bloquear un objeto. Si otro subproceso intenta bloquear ese mismo objeto, se bloquea hasta que se libera el primer subproceso. Use SyncLock cuidadosamente, ya que los problemas pueden deberse al uso indebido de SyncLock. Por ejemplo, este comando puede impedir las condiciones de carrera, pero provocar interbloqueos.

  • InterLocked. Permite un conjunto seleccionado de operaciones seguras para subprocesos en variables numéricas básicas.

Para obtener más información, consulte Subprocesos y subprocesos.