Gewusst wie: Verwenden von Threadpools (C#-Programmierhandbuch)

Aktualisiert: November 2007

Ein Threadpool ist eine Threadauflistung, mit der verschiedene Aufgaben im Hintergrund ausgeführt werden können. (Hintergrundinformationen finden Sie unter Verwenden von Threading (C#-Programmierhandbuch).) Auf diese Weise kann der primäre Thread asynchron andere Aufgaben ausführen.

Threadpools werden häufig in Serveranwendungen verwendet. Jede eingehende Anforderung wird einem Thread aus dem Threadpool zugewiesen, sodass die Anforderung asynchron verarbeitet werden kann, ohne den primären Thread zu binden oder die Verarbeitung von nachfolgenden Anforderungen zu verzögern.

Sobald ein Thread im Pool seine Aufgabe beendet hat, wird er wieder in die Threadwarteschlange eingereiht, von wo aus er wiederverwendet werden kann. Durch diese erneute Verwendung entfällt in Anwendungen der Aufwand zum Erstellen eines neuen Threads für jede Aufgabe.

Threadpools haben in der Regel eine maximale Anzahl von Threads. Wenn alle Threads aktiv sind, werden weitere Aufgaben in die Warteschlange gestellt, bis sie von wieder verfügbaren Threads verarbeitet werden können.

Sie können einen eigenen Threadpool implementieren, es ist jedoch einfacher, mit der ThreadPool-Klasse den von .NET Framework bereitgestellten Threadpool zu verwenden.

Im folgenden Beispiel wird mithilfe des Threadpools von .NET Framework das Fibonacci-Ergebnis für zehn Zahlen zwischen 20 und 40 berechnet. Alle Fibonacci-Ergebnisse werden durch die Fibonacci-Klasse dargestellt, die eine Methode namens ThreadPoolCallback bereitstellt, von der die Berechnung durchgeführt wird. Es wird ein Objekt erstellt, das den jeweiligen Fibonacci-Wert darstellt, und die ThreadPoolCallback-Methode wird an QueueUserWorkItem übergeben. Dadurch wird ein verfügbarer Thread im Pool zugewiesen, um die Methode auszuführen.

Da jedem Fibonacci-Objekt ein halb zufälliger Wert zur Berechnung zugewiesen wird und alle Threads um Prozessorzeit konkurrieren, lässt sich nicht vorhersagen, wie lange die Berechnung aller zehn Ergebnisse dauert. Aus diesem Grund wird an jedes Fibonacci-Objekt während der Erstellung eine Instanz der ManualResetEvent-Klasse übergeben. Jedes Objekt signalisiert dem bereitgestellten Ereignisobjekt die Fertigstellung seiner Berechnung. Auf diese Weise kann der primäre Thread die Ausführung mit WaitAll blockieren, bis alle zehn Fibonacci-Objekte ein Ergebnis berechnet haben. Die Main-Methode zeigt dann alle Fibonacci-Ergebnisse an.

Beispiel

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);
        }
    }
}

Die Ausgabe lautet wie folgt:

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

Siehe auch

Aufgaben

Technologiebeispiel für Monitor-Synchronisierung

Technologiebeispiel für Wait-Synchronisierung

Konzepte

C#-Programmierhandbuch

Monitore

Referenz

Threading (C#-Programmierhandbuch)

Verwenden von Threading (C#-Programmierhandbuch)

Mutex

WaitAll

ManualResetEvent

Set

ThreadPool

QueueUserWorkItem

ManualResetEvent

Weitere Ressourcen

Gewusst wie: Synchronisieren des Zugriffs auf eine freigegebene Ressource in einer Multithreadumgebung mithilfe von Visual C# .NET

Sicherheit in .NET Framework