Compartir a través de


Procedimientos recomendados para subprocesamiento administrado

El subprocesamiento múltiple requiere que la programación sea cuidadosa. La complejidad de muchas tareas se puede reducir poniendo las solicitudes de ejecución en cola por subprocesos del grupo de subprocesos. En este tema se tratan situaciones más complicadas, como coordinar el trabajo de múltiples subprocesos, o controlar los subprocesos que se bloquean.

Para más instrucciones sobre cómo implementar el subprocesamiento múltiple en las bibliotecas de clases, vea Instrucciones de diseño de subprocesamiento.

Bloqueos y condiciones de anticipación

El subprocesamiento múltiple resuelve problemas de rendimiento y de capacidad de respuesta, pero al hacerlo también crea nuevos problemas, como bloqueos y condiciones de anticipación.

Bloqueos

Un bloqueo tiene lugar cuando dos subprocesos intentan bloquear un recurso que ya ha bloqueado uno de estos subprocesos. Ninguno de los subprocesos puede avanzar.

Muchos métodos de las clases del subprocesamiento administrado ofrecen tiempos de espera que se utilizan para detectar bloqueos. Por ejemplo, con el siguiente código se intenta bloquear la instancia en curso. Si el bloqueo no se consigue en 300 milisegundos, Monitor.TryEnter devuelve el valor false.

If Monitor.TryEnter(Me, 300) Then
    Try
        ' Place code protected by the Monitor here.
    Finally
        Monitor.Exit(Me)
    End Try
Else
    ' Code to execute if the attempt times out.
End If
[C#]
if (Monitor.TryEnter(this, 300)) {
    try {
        // Place code protected by the Monitor here.
    }
    finally {
        Monitor.Exit(this);
    }
}
else {
    // Code to execute if the attempt times out.
}

Condiciones de anticipación

Una condición de anticipación es un error que se produce cuando el resultado de un programa depende del primero de dos o más subprocesos que consiga llegar hasta un bloque específico de código. Ejecutar el programa muchas veces genera distintos resultados y no es posible predecir el resultado de una ejecución específica.

Un ejemplo sencillo de una condición de anticipación es el incremento de un campo. Suponga una clase que tiene un campo privado static (Shared en Visual Basic) que se incrementa cada vez que se crea una instancia de la clase, mediante código como objCt++; (C#) o objCt += 1 (Visual Basic). Esta operación requiere cargar el valor de objCt en un registro, incrementar el valor y almacenarlo en objCt.

En una aplicación multiproceso, un subproceso que realiza los tres pasos puede adelantar al subproceso que ha cargado e incrementado el valor; cuando el primer subproceso reanuda la ejecución y almacena su valor, sobrescribe objCt sin tener en cuenta el hecho de que el valor ha cambiado mientras tanto.

Esta particular condición de anticipación se puede evitar fácilmente con los métodos de la clase Interlocked, como Interlocked.Increment. Para más información sobre otras técnicas de sincronización de datos entre varios subprocesos, vea Sincronizar datos para subprocesamiento múltiple.

También se pueden producir condiciones de anticipación al sincronizar las actividades de varios subprocesos. Siempre que escriba una línea de código, debe tener en cuenta qué puede ocurrir si otro subproceso adelanta a un subproceso antes de ejecutar la línea (o antes de cualquiera de las instrucciones máquina que forman la línea).

Número de procesadores

El subprocesamiento múltiple resuelve distintos problemas en equipos de un solo procesador que ejecuten software de usuario final, y en equipos multiprocesador que se utilicen normalmente como servidores.

Equipos de un solo procesador

El subprocesamiento múltiple ofrece una gran capacidad de respuesta al usuario del equipo, y utiliza el tiempo de inactividad para realizar tareas en segundo plano. Si utiliza el subprocesamiento múltiple en un equipo de un solo procesador:

  • Sólo se ejecuta un subproceso cada vez.
  • Se ejecuta un subproceso en segundo plano sólo cuando el subproceso principal del usuario está inactivo. Un subproceso en primer plano que se ejecuta continuamente agota el tiempo de procesador de los subprocesos en segundo plano.
  • Cuando se llama al método Thread.Start en un subproceso, ese subproceso no se ejecuta hasta que el subproceso en curso le cede la ejecución o es adelantado por el sistema operativo.
  • Generalmente, las condiciones de anticipación se producen debido a que el programador no tuvo en cuenta el hecho de que un subproceso puede ser adelantado por otro en un momento difícil permitiendo, algunas veces, que otro subproceso llegue el primero al bloque de código.

Equipos multiprocesador

El subprocesamiento múltiple proporciona un mayor rendimiento. Diez procesadores pueden hacer diez veces el trabajo de uno, pero sólo si el trabajo se divide de forma que los diez trabajen al mismo tiempo; los subprocesos proporcionan una forma fácil de dividir el trabajo y de aprovechar la eficacia de procesamiento adicional. Si utiliza el subprocesamiento múltiple en un equipo multiprocesador:

  • El número de subprocesos que se pueden ejecutar de forma simultánea está limitado por el número de procesadores.
  • Sólo se ejecuta un subproceso en segundo plano si el número de subprocesos que se ejecutan en primer en plano es menor que el número de procesadores.
  • Cuando se llama al método Thread.Start en un subproceso, ese subproceso se puede ejecutar o no inmediatamente dependiendo del número de procesadores y de subprocesos en espera de ejecución.
  • Las condiciones de anticipación se pueden producir no sólo debido a las prioridades imprevistas de subprocesos, sino también porque dos subprocesos que se ejecutan en procesadores diferentes pueden competir para alcanzar el mismo bloque de código.

Recomendaciones generales

Tenga en cuenta las siguientes instrucciones cuando utilice varios subprocesos:

  • No utilice Thread.Abort para finalizar otros subprocesos. Una llamada a Abort en otro subproceso es similar a iniciar una excepción en ese subproceso, sin conocer qué punto ha alcanzado en su procesamiento.
  • No utilice Thread.Suspend ni Thread.Resume para sincronizar las actividades de varios subprocesos. Utilice Mutex, ManualResetEvent, AutoResetEvent y Monitor.
  • No controle la ejecución de subprocesos de trabajo desde el programa principal (con eventos, por ejemplo). En su lugar, diseñe un programa de forma que los subprocesos de trabajo sean los que tengan que esperar hasta que haya trabajo disponible, lo ejecuten y notifiquen su finalización a otras partes del programa. Si los subprocesos de trabajo no se bloquean, considere la utilización de subprocesos del grupo de subprocesos. El uso de Monitor.PulseAll es útil en situaciones en las que los subprocesos de trabajo se bloquean.
  • Asegúrese de que un subproceso que entra en un monitor siempre sale de ese monitor, aun en el caso de que se produzca una excepción mientras el subproceso se encuentra en el monitor. La instrucción lock de C# y la instrucción SyncLock de Visual Basic ofrecen automáticamente este comportamiento mediante un bloque finally que garantiza que se llama a Monitor.Exit. Si no está seguro de que se llamará a Exit, considere la posibilidad de cambiar el diseño con el fin de utilizar Mutex. Una zona de exclusión mutua se libera automáticamente cuando finaliza el subproceso al que pertenece.
  • Utilice varios subprocesos para tareas que requieren recursos diferentes, y evite asignar varios subprocesos a un solo recurso. Por ejemplo, en tareas que impliquen beneficios de E/S por tener un subproceso propio, ya que ese subproceso se bloquea durante las operaciones de E/S y, de este modo, permite ejecutar otros subprocesos. Los datos proporcionados por el usuario son otro recurso que se beneficia de la utilización de un subproceso dedicado. En un equipo de un solo procesador, una tarea que implica un cálculo intensivo coexiste con los datos proporcionados por el usuario y con tareas que implican la E/S, pero varias tareas de cálculo intensivo compiten entre ellas.

Vea también

Instrucciones de diseño de subprocesamiento | Subprocesos | Subprocesos y subprocesamiento