Verwalteter Threadpool

Aktualisiert: November 2007

Die ThreadPool-Klasse stellt einer Anwendung einen Pool von Arbeitsthreads bereit, die vom System verwaltet werden. Dadurch werden Sie von der Threadverwaltung entlastet und können sich stärker auf Anwendungsaufgaben konzentrieren. Für kurze Aufgaben, bei denen Hintergrundverarbeitung erforderlich ist, bietet sich der verwaltete Threadpool als einfache Lösung für den Umgang mit mehreren Threads an.

Hinweis:

In .NET Framework Version 2.0 Service Pack 1 wurde der Durchsatz des Threadpools in drei wichtigen Bereichen, die in früheren Versionen von .NET Framework als Engpässe galten, deutlich gesteigert: Einfügen von Aufgaben in die Warteschlange, Weiterleitung von Threads im Threadpool und Weiterleitung von E/A-Abschlussthreads. Zur Verwendung dieser Funktionen sollte die Anwendung für .NET Framework Version 3.5 ausgelegt sein. Weitere Informationen finden Sie unter Architektur von .NET Framework 3.5.

In .NET Framework, Version 2.0, steht für Hintergrundaufgaben, die mit der Benutzeroberfläche interagieren, auch die BackgroundWorker-Klasse zur Verfügung, die mithilfe von Ereignissen kommuniziert, die für den Benutzeroberflächenthread ausgelöst werden.

Threadpoolthreads werden in .NET Framework zu verschiedenen Zwecken eingesetzt, z. B. für asynchrone E/A-Komplettierung, Zeitgeberrückrufe, registrierte Wartevorgänge, asynchrone Methodenaufrufe mithilfe von Delegaten und System.Net-Socketverbindungen.

Gründe, die gegen die Verwendung von Threadpoolthreads sprechen

In einigen Szenarien ist die Erstellung und Verwaltung eigener Threads der Verwendung von Threadpoolthreads vorzuziehen:

  • Sie benötigen einen Vordergrundthread.

  • Sie benötigen einen Thread mit einer bestimmten Priorität.

  • Es gibt Aufgaben, die den Thread über einen längeren Zeitraum blockieren. Da die Anzahl der Threads im Threadpool begrenzt ist, kann eine hohe Anzahl blockierter Threadpoolthreads das Starten von Aufgaben verhindern.

  • Sie müssen Threads in ein Singlethread-Apartment einfügen. Alle ThreadPool-Threads befinden sich im Multithread-Apartment.

  • Dem Thread muss eine stabile Identität zugeordnet werden, oder ein Thread soll einer Aufgabe zugeordnet werden.

Eigenschaften von Threadpools

Threadpoolthreads sind Hintergrundthreads. Informationen hierzu finden Sie unter Vordergrund- und Hintergrundthreads. Jeder Thread verwendet die standardmäßige Stapelgröße, wird mit Standardpriorität ausgeführt und befindet sich im Multithread-Apartment.

Pro Prozess gibt es nur einen Threadpool.

Ausnehmen in Threadpoolthreads

Unbehandelte Ausnahmen von Threadpoolthreads beenden den Prozess. Für diese Regel gelten jedoch die folgenden drei Ausnahmen:

  • In einem Threadpoolthread wird eine ThreadAbortException ausgelöst, da Abort aufgerufen wurde.

  • In einem Threadpoolthread wird eine AppDomainUnloadedException ausgelöst, da die Anwendungsdomäne entladen wird.

  • Der Prozess wurde durch die Common Language Runtime oder einen Hostprozess beendet.

Weitere Informationen finden Sie unter Ausnahmen in verwalteten Threads.

Hinweis:

In den .NET Framework-Versionen 1.0 und 1.1 erfasst die Common Language Runtime unbehandelte Ausnahmen in Threadpoolthreads automatisch. Dies kann den Anwendungszustand beschädigen und dazu führen, dass die Anwendung nicht mehr reagiert. Das Debuggen kann erhebliche Schwierigkeiten bereiten.

Maximale Anzahl von Threadpoolthreads

Die Anzahl von Vorgängen, die für den Threadpool in die Warteschlange aufgenommen werden, ist zwar nur durch den verfügbaren Speicher begrenzt, der Threadpool kann jedoch nur eine bestimmte Anzahl von aktiven Threads enthalten. Die Anzahl ist standardmäßig auf 25 Arbeitsthreads pro CPU und 1.000 E/A-Komplettierung-Threads begrenzt.

Sie können die maximale Anzahl von Threads mithilfe der GetMaxThreads-Methode und der SetMaxThreads-Methode steuern.

Hinweis:

In den .NET Framework-Versionen 1.0 und 1.1 kann die Größe des Threadpools nicht über verwalteten Code festgelegt werden. Code, der die Common Language Runtime hostet, kann die Größe anhand von CorSetMaxThreads festlegen, das in mscoree.h definiert wird.

Mindestanzahl von Threads im Leerlauf

Der Threadpool behält eine Mindestanzahl verfügbarer Threads bei, auch wenn sich diese alle im Leerlauf befinden, damit Aufgaben in der Warteschlange sofort gestartet werden können. Threads im Leerlauf, die diese Mindestanzahl überschreiten, werden beendet, um die Systemressourcen zu schonen. Standardmäßig wird pro Prozessor ein Thread im Leerlauf beibehalten.

Der Threadpool startet neue Threads im Leerlauf erst nach einer vorgegebenen Verzögerung (eine halbe Sekunde in .NET Framework, Version 2.0). Wenn die Anwendung innerhalb eines kurzen Zeitraums regelmäßig viele Aufgaben startet, kann schon eine geringfügige Erhöhung der Anzahl von Threads im Leerlauf den Durchsatz erheblich steigern. Eine zu hohe Anzahl von Threads im Leerlauf belastet die Systemressourcen unnötig.

Sie können die Anzahl von Threads im Leerlauf, die im Threadpool beibehalten werden, mithilfe der GetMinThreads-Methode und der SetMinThreads-Methode steuern.

Hinweis:

In .NET Framework, Version 1.0, kann keine Mindestanzahl von Threads im Leerlauf festgelegt werden.

Überspringen von Sicherheitsüberprüfungen

Der Threadpool stellt auch die ThreadPool.UnsafeQueueUserWorkItem-Methode und die ThreadPool.UnsafeRegisterWaitForSingleObject-Methode bereit. Verwenden Sie diese Methoden nur, wenn Sie genau wissen, dass der Stapel des Aufrufers für Sicherheitsüberprüfungen irrelevant ist, die während der Ausführung der in der Warteschlange befindlichen Aufgabe erfolgen. Der Stapel des Aufrufers wird sowohl bei QueueUserWorkItem als auch bei RegisterWaitForSingleObject aufgezeichnet und zu Beginn der Ausführung einer Aufgabe mit dem Stapel des Threadpoolthreads zusammengeführt. Wenn eine Sicherheitsüberprüfung erforderlich ist, muss der gesamte Stapel überprüft werden. Obwohl die Überprüfung Sicherheit gewährleistet, wird dadurch auch die Leistung beeinträchtigt.

Verwenden des Threadpools

Für die Verwendung des Threadpools wird ThreadPool.QueueUserWorkItem aus verwaltetem Code (oder CorQueueUserWorkItem aus nicht verwaltetem Code) aufgerufen und ein WaitCallback-Delegat übergeben, der die Methode darstellt, die die Aufgabe ausführt. Sie können auch auf einen Wartevorgang bezogene Arbeitsaufgaben in die Warteschlange aufnehmen, indem Sie die ThreadPool.RegisterWaitForSingleObject-Methode verwenden und ein WaitHandle übergeben. Wird dieses ausgelöst oder das Zeitlimit überschritten, wird die vom WaitOrTimerCallback-Delegaten dargestellte Methode aufgerufen. Der Threadpool ruft die Rückrufmethode in beiden Fällen über einen Hintergrundthread auf.

ThreadPool-Beispiele

In den drei folgenden Codebeispielen werden die QueueUserWorkItem-Methode und die RegisterWaitForSingleObject-Methode veranschaulicht.

Im ersten Beispiel wird mithilfe der QueueUserWorkItem-Methode eine sehr einfache Aufgabe, die durch die ThreadProc-Methode dargestellt wird, in die Warteschlange eingefügt.

Imports System
Imports System.Threading

Public Class Example
    Public Shared Sub Main()
        ' Queue the task.
        ThreadPool.QueueUserWorkItem( _
            New WaitCallback(AddressOf ThreadProc))
        
        Console.WriteLine("Main thread does some work, then sleeps.")
        ' If you comment out the Sleep, the main thread exits before
        ' the thread pool task runs.  The thread pool uses background
        ' threads, which do not keep the application running.  (This
        ' is a simple example of a race condition.)
        Thread.Sleep(1000)

        Console.WriteLine("Main thread exits.")
    End Sub

    ' This thread procedure performs the task.
    Shared Sub ThreadProc(stateInfo As Object)
        ' No state object was passed to QueueUserWorkItem, so 
        ' stateInfo is null.
        Console.WriteLine("Hello from the thread pool.")
    End Sub
End Class
using System;
using System.Threading;
public class Example {
    public static void Main() {
        // Queue the task.
        ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
        
        Console.WriteLine("Main thread does some work, then sleeps.");
        // If you comment out the Sleep, the main thread exits before
        // the thread pool task runs.  The thread pool uses background
        // threads, which do not keep the application running.  (This
        // is a simple example of a race condition.)
        Thread.Sleep(1000);

        Console.WriteLine("Main thread exits.");
    }

    // This thread procedure performs the task.
    static void ThreadProc(Object stateInfo) {
        // No state object was passed to QueueUserWorkItem, so 
        // stateInfo is null.
        Console.WriteLine("Hello from the thread pool.");
    }
}

Bereitstellen von Aufgabendaten für "QueueUserWorkItem"

Im folgenden Codebeispiel wird eine Aufgabe mithilfe der QueueUserWorkItem-Methode in die Warteschlange eingefügt, und es werden die Daten für die Aufgabe bereitgestellt.

Imports System
Imports System.Threading
' TaskInfo holds state information for a task that will be
' executed by a ThreadPool thread.
Public Class TaskInfo
    ' State information for the task.  These members
    ' can be implemented as read-only properties, read/write
    ' properties with validation, and so on, as required.
    Public Boilerplate As String
    Public Value As Integer

    ' Public constructor provides an easy way to supply all
    ' the information needed for the task.
    Public Sub New(text As String, number As Integer)
        Boilerplate = text
        Value = number
    End Sub
End Class

Public Class Example
    Public Shared Sub Main()
        ' Create an object containing the information needed
        ' for the task.
        Dim ti As New TaskInfo("This report displays the number {0}.", 42)

        ' Queue the task and data.
        If ThreadPool.QueueUserWorkItem( _
            New WaitCallback(AddressOf ThreadProc), ti) Then
        
            Console.WriteLine("Main thread does some work, then sleeps.")

            ' If you comment out the Sleep, the main thread exits before
            ' the ThreadPool task has a chance to run.  ThreadPool uses 
            ' background threads, which do not keep the application 
            ' running.  (This is a simple example of a race condition.)
            Thread.Sleep(1000)

            Console.WriteLine("Main thread exits.")
        Else
            Console.WriteLine("Unable to queue ThreadPool request.")
        End If
    End Sub

    ' The thread procedure performs the independent task, in this case
    ' formatting and printing a very simple report.
    '
    Shared Sub ThreadProc(stateInfo As Object)
        Dim ti As TaskInfo = CType(stateInfo, TaskInfo)
        Console.WriteLine(ti.Boilerplate, ti.Value)
    End Sub
End Class
using System;
using System.Threading;

// TaskInfo holds state information for a task that will be
// executed by a ThreadPool thread.
public class TaskInfo {
    // State information for the task.  These members
    // can be implemented as read-only properties, read/write
    // properties with validation, and so on, as required.
    public string Boilerplate;
    public int Value;

    // Public constructor provides an easy way to supply all
    // the information needed for the task.
    public TaskInfo(string text, int number) {
        Boilerplate = text;
        Value = number;
    }
}

public class Example {
    public static void Main() {
        // Create an object containing the information needed
        // for the task.
        TaskInfo ti = new TaskInfo("This report displays the number {0}.", 42);

        // Queue the task and data.
        if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), ti)) {    
            Console.WriteLine("Main thread does some work, then sleeps.");

            // If you comment out the Sleep, the main thread exits before
            // the ThreadPool task has a chance to run.  ThreadPool uses 
            // background threads, which do not keep the application 
            // running.  (This is a simple example of a race condition.)
            Thread.Sleep(1000);

            Console.WriteLine("Main thread exits.");
        }
        else {
            Console.WriteLine("Unable to queue ThreadPool request."); 
        }
    }

    // The thread procedure performs the independent task, in this case
    // formatting and printing a very simple report.
    //
    static void ThreadProc(Object stateInfo) {
        TaskInfo ti = (TaskInfo) stateInfo;
        Console.WriteLine(ti.Boilerplate, ti.Value); 
    }
}

RegisterWaitForSingleObject

Im folgenden Beispiel werden verschiedene Threadingfeatures dargestellt.

Imports System
Imports System.Threading

' TaskInfo contains data that will be passed to the callback
' method.
Public Class TaskInfo
    public Handle As RegisteredWaitHandle = Nothing
    public OtherInfo As String = "default"
End Class

Public Class Example
    Public Shared Sub Main()
        ' The main thread uses AutoResetEvent to signal the
        ' registered wait handle, which executes the callback
        ' method.
        Dim ev As New AutoResetEvent(false)

        Dim ti As New TaskInfo()
        ti.OtherInfo = "First task"
        ' The TaskInfo for the task includes the registered wait
        ' handle returned by RegisterWaitForSingleObject.  This
        ' allows the wait to be terminated when the object has
        ' been signaled once (see WaitProc).
        ti.Handle = ThreadPool.RegisterWaitForSingleObject( _
            ev, _
            New WaitOrTimerCallback(AddressOf WaitProc), _
            ti, _
            1000, _
            false _
        )

        ' The main thread waits about three seconds, to demonstrate 
        ' the time-outs on the queued task, and then signals.
        Thread.Sleep(3100)
        Console.WriteLine("Main thread signals.")
        ev.Set()

        ' The main thread sleeps, which should give the callback
        ' method time to execute.  If you comment out this line, the
        ' program usually ends before the ThreadPool thread can execute.
        Thread.Sleep(1000)
        ' If you start a thread yourself, you can wait for it to end
        ' by calling Thread.Join.  This option is not available with 
        ' thread pool threads.
    End Sub
   
    ' The callback method executes when the registered wait times out,
    ' or when the WaitHandle (in this case AutoResetEvent) is signaled.
    ' WaitProc unregisters the WaitHandle the first time the event is 
    ' signaled.
    Public Shared Sub WaitProc(state As Object, timedOut As Boolean)
        ' The state object must be cast to the correct type, because the
        ' signature of the WaitOrTimerCallback delegate specifies type
        ' Object.
        Dim ti As TaskInfo = CType(state, TaskInfo)

        Dim cause As String = "TIMED OUT"
        If Not timedOut Then
            cause = "SIGNALED"
            ' If the callback method executes because the WaitHandle is
            ' signaled, stop future execution of the callback method
            ' by unregistering the WaitHandle.
            If Not ti.Handle Is Nothing Then
                ti.Handle.Unregister(Nothing)
            End If
        End If 

        Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.", _
            ti.OtherInfo, _
            Thread.CurrentThread.GetHashCode().ToString(), _
            cause _
        )
    End Sub
End Class
using System;
using System.Threading;

// TaskInfo contains data that will be passed to the callback
// method.
public class TaskInfo {
    public RegisteredWaitHandle Handle = null;
    public string OtherInfo = "default";
}

public class Example {
    public static void Main(string[] args) {
        // The main thread uses AutoResetEvent to signal the
        // registered wait handle, which executes the callback
        // method.
        AutoResetEvent ev = new AutoResetEvent(false);

        TaskInfo ti = new TaskInfo();
        ti.OtherInfo = "First task";
        // The TaskInfo for the task includes the registered wait
        // handle returned by RegisterWaitForSingleObject.  This
        // allows the wait to be terminated when the object has
        // been signaled once (see WaitProc).
        ti.Handle = ThreadPool.RegisterWaitForSingleObject(
            ev,
            new WaitOrTimerCallback(WaitProc),
            ti,
            1000,
            false
        );

        // The main thread waits three seconds, to demonstrate the
        // time-outs on the queued thread, and then signals.
        Thread.Sleep(3100);
        Console.WriteLine("Main thread signals.");
        ev.Set();

        // The main thread sleeps, which should give the callback
        // method time to execute.  If you comment out this line, the
        // program usually ends before the ThreadPool thread can execute.
        Thread.Sleep(1000);
        // If you start a thread yourself, you can wait for it to end
        // by calling Thread.Join.  This option is not available with 
        // thread pool threads.
    }
   
    // The callback method executes when the registered wait times out,
    // or when the WaitHandle (in this case AutoResetEvent) is signaled.
    // WaitProc unregisters the WaitHandle the first time the event is 
    // signaled.
    public static void WaitProc(object state, bool timedOut) {
        // The state object must be cast to the correct type, because the
        // signature of the WaitOrTimerCallback delegate specifies type
        // Object.
        TaskInfo ti = (TaskInfo) state;

        string cause = "TIMED OUT";
        if (!timedOut) {
            cause = "SIGNALED";
            // If the callback method executes because the WaitHandle is
            // signaled, stop future execution of the callback method
            // by unregistering the WaitHandle.
            if (ti.Handle != null)
                ti.Handle.Unregister(null);
        } 

        Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
            ti.OtherInfo, 
            Thread.CurrentThread.GetHashCode().ToString(), 
            cause
        );
    }
}

Siehe auch

Konzepte

Threads und Threading

Asynchrone Datei-E/A

Zeitgeber

Referenz

ThreadPool

Weitere Ressourcen

Threadingobjekte und -features