Compartir a través de


Cómo: Utilizar un grupo de subprocesos (Guía de programación de C#)

Actualización: noviembre 2007

Un grupo de subprocesos es una colección de subprocesos que se pueden utilizar para realizar varias tareas en segundo plano. (Vea Utilizar el subprocesamiento (Guía de programación de C#) para obtener información básica.) Esto deja libre el subproceso primario para realizar otras tareas de forma asincrónica.

Los grupos de subprocesos se emplean a menudo en aplicaciones de servidor. Cada solicitud entrante se asigna a un subproceso del grupo, a fin de que la solicitud se pueda procesar de forma asincrónica, sin paralizar el subproceso primario ni retrasar el procesamiento de las solicitudes siguientes.

Cuando un subproceso del grupo finaliza su tarea, se devuelve a una cola de subprocesos en espera, donde se puede reutilizar. Esta reutilización permite a las aplicaciones evitar el costo de crear un nuevo subproceso para cada tarea.

Los grupos de subprocesos tienen normalmente un número máximo de subprocesos. Si todos los subprocesos están ocupados, las tareas adicionales se colocan en cola hasta que se puedan atender a medida que los subprocesos queden disponibles.

Puede implementar su propio grupo de subprocesos, pero es más fácil utilizar el que proporciona .NET Framework a través de la clase ThreadPool.

El ejemplo siguiente utiliza el grupo de subprocesos de .NET Framework para calcular el resultado de Fibonacci para diez números entre 20 y 40. Cada resultado de Fibonacci se representa mediante la clase Fibonacci, que proporciona un método denominado ThreadPoolCallback que realiza el cálculo. Se crea un objeto que representa cada valor de Fibonacci y se pasa el método ThreadPoolCallback a QueueUserWorkItem, que asigna un subproceso disponible en el grupo para ejecutar el método.

Debido a que se asigna un valor semialeatorio a cada objeto Fibonacci para el cálculo, y puesto que cada uno de los subprocesos compite para obtener tiempo del procesador, no se puede saber de antemano cuánto tiempo llevará calcular los diez resultados. Por eso se pasa a cada objeto Fibonacci una instancia de la clase ManualResetEvent durante la construcción. Cada objeto señaliza el objeto de evento proporcionado cuando se completa el cálculo, lo que permite al subproceso primario bloquear la ejecución con WaitAll hasta que los diez objetos Fibonacci han calculado un resultado. A continuación, el método Main muestra cada resultado de Fibonacci.

Ejemplo

using System;
using System.Threading;

public class Fibonacci
{
    public Fibonacci(int n, ManualResetEvent doneEvent)
    {
        _n = n;
        _doneEvent = doneEvent;
    }

    // Wrapper method for use with thread pool.
    public void ThreadPoolCallback(Object threadContext)
    {
        int threadIndex = (int)threadContext;
        Console.WriteLine("thread {0} started...", threadIndex);
        _fibOfN = Calculate(_n);
        Console.WriteLine("thread {0} result calculated...", threadIndex);
        _doneEvent.Set();
    }

    // Recursive method that calculates the Nth Fibonacci number.
    public int Calculate(int n)
    {
        if (n <= 1)
        {
            return n;
        }

        return Calculate(n - 1) + Calculate(n - 2);
    }

    public int N { get { return _n; } }
    private int _n;

    public int FibOfN { get { return _fibOfN; } }
    private int _fibOfN;

    private ManualResetEvent _doneEvent;
}

public class ThreadPoolExample
{
    static void Main()
    {
        const int FibonacciCalculations = 10;

        // One event is used for each Fibonacci object
        ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations];
        Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations];
        Random r = new Random();

        // Configure and launch threads using ThreadPool:
        Console.WriteLine("launching {0} tasks...", FibonacciCalculations);
        for (int i = 0; i < FibonacciCalculations; i++)
        {
            doneEvents[i] = new ManualResetEvent(false);
            Fibonacci f = new Fibonacci(r.Next(20,40), doneEvents[i]);
            fibArray[i] = f;
            ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
        }

        // Wait for all threads in pool to calculation...
        WaitHandle.WaitAll(doneEvents);
        Console.WriteLine("All calculations are complete.");

        // Display the results...
        for (int i= 0; i<FibonacciCalculations; i++)
        {
            Fibonacci f = fibArray[i];
            Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN);
        }
    }
}

Éste es el resultado:

launching 10 tasks...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
all calculations complete
Fibonacci(22) = 17711
Fibonacci(25) = 75025
Fibonacci(32) = 2178309
Fibonacci(36) = 14930352
Fibonacci(32) = 2178309
Fibonacci(26) = 121393
Fibonacci(35) = 9227465
Fibonacci(23) = 28657
Fibonacci(39) = 63245986
Fibonacci(22) = 17711

Vea también

Tareas

Ejemplo Monitor Synchronization Technology

Ejemplo Wait Synchronization Technology

Conceptos

Guía de programación de C#

Monitores

Referencia

Subprocesamiento (Guía de programación de C#)

Utilizar el subprocesamiento (Guía de programación de C#)

Mutex

WaitAll

ManualResetEvent

Set

ThreadPool

QueueUserWorkItem

ManualResetEvent

Otros recursos

CÓMO: Sincronizar el acceso a un recurso compartido en un entorno de subprocesamiento utilizando Visual C# .NET

Seguridad en .NET Framework