Evaluar y enviar comentarios
Esta página es específica de
Microsoft Visual Studio 2005/.NET Framework 2.0

Hay además otras versiones disponibles para:
Guía de programación de C#
Cómo: Crear y terminar subprocesos (Guía de programación de C#)

En este ejemplo se muestra cómo crear un subproceso auxiliar o de trabajo, y utilizarlo para realizar el procesamiento en paralelo con el subproceso primario. También se muestra cómo hacer que un subproceso espere a que termine la ejecución de otro y, a continuación, finalice correctamente. Para obtener información adicional sobre el multiprocesamiento, vea Subprocesamiento administrado y Utilizar el subprocesamiento (Guía de programación de C#).

El ejemplo crea una clase denominada Worker que contiene el método que el subproceso de trabajo ejecutará, denominado DoWork. Ésta es esencialmente la función Main para el subproceso de trabajo. El subproceso de trabajo comenzará la ejecución llamando a este método y finalizará automáticamente cuando el método devuelva un resultado. El método DoWork tiene la apariencia siguiente:

C#
public void DoWork()
{
    while (!_shouldStop)
    {
        Console.WriteLine("worker thread: working...");
    }
    Console.WriteLine("worker thread: terminating gracefully.");
}

La clase Worker contiene un método adicional que se utiliza para indicar a DoWork que debe devolver un resultado. Este método se denomina RequestStop y tiene la apariencia siguiente:

C#
public void RequestStop()
{
    _shouldStop = true;
}

El método RequestStop asigna simplemente el miembro de datos _shouldStop a true. Como el método DoWork comprueba el miembro de datos, esto tiene el efecto indirecto de que DoWork devuelva un resultado, con lo que termina el subproceso de trabajo. Sin embargo, es importante tener en cuenta que DoWork y RequestStop son ejecutados por subprocesos diferentes. El subproceso de trabajo ejecuta DoWork y el subproceso primario ejecuta RequestStop, por lo que el miembro de datos _shouldStop se declara volatile, de la forma siguiente:

C#
private volatile bool _shouldStop;

La palabra clave volatile alerta al compilador de que varios subprocesos tendrán acceso al miembro de datos _shouldStop y, en consecuencia, no debe asumir que el estado de optimización del miembro será óptimo. Para obtener más información, vea volatile (Referencia de C#).

El uso de volatile con el miembro de datos _shouldStop permite el acceso seguro de varios subprocesos a este miembro sin utilizar técnicas formales de sincronización de subprocesos, pero sólo porque _shouldStop es un valor de tipo bool. Esto significa que sólo es necesario utilizar operaciones atómicas únicas para modificar _shouldStop. Sin embargo, si este miembro de datos fuera una clase, estructura o matriz, el acceso a él de varios subprocesos probablemente produciría daños intermitentes en los datos. Considere un subproceso que cambia los valores en una matriz. Windows interrumpe con regularidad los subprocesos para permitir a otros subprocesos que se ejecuten, de modo que este subproceso se detendría después de asignar ciertos elementos de la matriz pero antes de asignar otros. Esto significa que la matriz tiene ahora un estado que el programador nunca previó y, como resultado, cuando otro subproceso lea la matriz se producirá un error.

Antes de crear realmente el subproceso de trabajo, la función Main crea un objeto Worker y una instancia de Thread. El objeto de subproceso se configura para utilizar el método Worker.DoWork como punto de entrada mediante el paso de una referencia a este método al constructor Thread, de la forma siguiente:

C#
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);

En este momento, aunque el objeto de subproceso de trabajo existe y está configurado, no se ha creado el subproceso de trabajo real todavía. Esto no ocurre hasta que Main llamada al método Start:

C#
workerThread.Start();

Entonces, el sistema inicia la ejecución del subproceso de trabajo, pero lo hace de forma asincrónica al subproceso primario. Esto significa que la función Main continúa ejecutando el código inmediatamente, mientras el subproceso de trabajo realiza simultáneamente la inicialización. Para garantizar que la función Main no intenta terminar el subproceso de trabajo antes de que pueda ejecutarse, la función Main entra en un bucle hasta que la propiedad IsAlive del objeto de subproceso de trabajo se establece en true:

C#
while (!workerThread.IsAlive);

A continuación, el subproceso primario se detiene brevemente con una llamada a Sleep. Esto asegura que la función DoWork del subproceso de trabajo ejecutará el bucle dentro del método DoWork durante unos pocos recorridos antes de que la función Main ejecute más comandos:

C#
Thread.Sleep(1);

Después de que transcurre un milisegundo, Main señala al objeto de subproceso de trabajo que debería finalizar utilizando el método Worker.RequestStop que se introdujo previamente:

C#
workerObject.RequestStop();

También es posible terminar un subproceso desde otro con una llamada a Abort, pero esto termina a la fuerza el subproceso afectado sin comprobar si ha completado su tarea y no proporciona ninguna oportunidad para la limpieza de recursos. Es preferible la técnica mostrada en este ejemplo.

Por último, la función Main llama al método Join en el objeto de subproceso de trabajo. Este método hace que el subproceso actual se bloquee, o espere, hasta que el subproceso que el objeto representa finalice. Por consiguiente, Join no devolverá ningún resultado hasta que el subproceso de trabajo vuelva, con lo cual finaliza:

C#
workerThread.Join();

En este momento, sólo existe el subproceso primario que ejecuta Main. Muestra un mensaje final y, después, vuelve y finaliza también el subproceso primario.

A continuación se muestra el ejemplo completo:

Ejemplo

C#
using System;
using System.Threading;

public class Worker
{
    // This method will be called when the thread is started.
    public void DoWork()
    {
        while (!_shouldStop)
        {
            Console.WriteLine("worker thread: working...");
        }
        Console.WriteLine("worker thread: terminating gracefully.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Volatile is used as hint to the compiler that this data
    // member will be accessed by multiple threads.
    private volatile bool _shouldStop;
}

public class WorkerThreadExample
{
    static void Main()
    {
        // Create the thread object. This does not start the thread.
        Worker workerObject = new Worker();
        Thread workerThread = new Thread(workerObject.DoWork);

        // Start the worker thread.
        workerThread.Start();
        Console.WriteLine("main thread: Starting worker thread...");

        // Loop until worker thread activates.
        while (!workerThread.IsAlive);

        // Put the main thread to sleep for 1 millisecond to
        // allow the worker thread to do some work:
        Thread.Sleep(1);

        // Request that the worker thread stop itself:
        workerObject.RequestStop();

        // Use the Join method to block the current thread 
        // until the object's thread terminates.
        workerThread.Join();
        Console.WriteLine("main thread: Worker thread has terminated.");
    }
}

Resultados del ejemplo

main thread: starting worker thread...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: terminating gracefully...
main thread: worker thread has terminated

Vea también

Contenido de la comunidad   ¿Qué es Community Content?
Agregar contenido nuevo RSS  Anotaciones
Processing
© 2008 Microsoft Corporation. Reservados todos los derechos. Términos de uso  |  Marcas Registradas  |  Privacidad
Page view tracker