Freigeben über


Verwalteter Threadpool

Aktualisiert: September 2010

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. Zum Beispiel können Sie beginnend mit .NET Framework, Version 4 Task-Objekte und Task<TResult>-Objekte erstellen, die asynchrone Aufgaben in Threads im Threadpool ausführen.

HinweisHinweis

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 oder höher ausgelegt sein.

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.

HinweisHinweis

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. Ab .NET Framework, Version 4 ist die Standardgröße des Threadpools für einen Prozess von mehreren Faktoren abhängig, z. B. von der Größe des virtuellen Adressraums. Ein Prozess kann die GetMaxThreads-Methode aufrufen, um die Anzahl der Threads zu bestimmen.

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

HinweisHinweis

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.

Threadpoolmindestwerte

Der Threadpool stellt neue Arbeitsthreads oder E/A-Abschlussthreads bei Bedarf bereit, bis ein angegebener Mindestwert für jede Kategorie erreicht ist. Sie können die GetMinThreads-Methode verwenden, um diese Mindestwerte abzurufen.

HinweisHinweis

Wenn die Anforderungen niedrig sind, kann die tatsächliche Anzahl der Threads im Threadpool unterhalb der Mindestwerte liegen.

Wenn ein Minimum erreicht wird, kann der Threadpool weitere Threads erstellen oder warten, bis einige Aufgaben abgeschlossen sind. Ab .NET Framework 4 erstellt und zerstört der Threadpool Arbeitsthreads, um den Durchsatz zu optimieren. Der Durchsatz ist als die Anzahl der Aufgaben definiert, die pro Zeiteinheit abgeschlossen werden. Bei zu wenigen Threads werden die verfügbaren Ressourcen möglicherweise nicht optimal ausgenutzt, wohingegen bei zu vielen Threads Ressourcenkonflikte auftreten können.

WarnhinweisVorsicht

Sie können die SetMinThreads-Methode verwenden, um die Mindestanzahl an Threads im Leerlauf zu erhöhen.Allerdings kann ein unnötiges Erhöhen dieses Wertes zu Leistungsproblemen führen.Wenn zu viele Aufgaben gleichzeitig gestartet werden, werden möglicherweise alle Aufgaben zu langsam ausgeführt.In den meisten Fällen erreicht der Threadpool mit dem eigenen Algorithmus für die Zuordnung von Threads eine bessere Leistung.

Ü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 sicher sind, dass der Stapel des Aufrufers irrelevant für die Sicherheitsüberprüfungen ist, die während der Ausführung der in der Warteschlange stehenden Aufgabe stattfinden. QueueUserWorkItem und RegisterWaitForSingleObject erfassen beide den Stapel des Aufrufers, der mit dem Stapel des Threadpoolthreads zusammengeführt wird, wenn der Thread beginnt, eine Aufgabe auszuführen. 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

Ab .NET Framework 4 kann der Threadpool am einfachsten über die Task Parallel Library verwendet werden. Standardmäßig verwenden parallele Bibliothekstypen wie Task und Task<TResult> Threadpoolthreads, um Aufgaben auszuführen. Sie können den Threadpool auch verwenden, indem Sie in verwaltetem Code ThreadPool.QueueUserWorkItem aufrufen (oder CorQueueUserWorkItem in nicht verwaltetem Code) und einen WaitCallback-Delegaten übergeben, der die Methode darstellt, die die Aufgabe ausführt. Eine andere Möglichkeit, den Threadpool zu verwenden, ist, Arbeitsaufgaben, die mit einem Wartevorgang verknüpft sind, mit der ThreadPool.RegisterWaitForSingleObject-Methode in die Warteschlange zu stellen und ein WaitHandle zu übergeben, das bei einer Signalisierung oder bei einem Timeout die Methode aufruft, die vom WaitOrTimerCallback-Delegaten dargestellt wird. Threadpoolthreads werden zum Aufrufen von Rückrufmethoden verwendet.

ThreadPool-Beispiele

Die Codebeispiele in diesem Abschnitt veranschaulichen den Threadpool durch Verwendung der Task-Klasse, der ThreadPool.QueueUserWorkItem-Methode und der ThreadPool.RegisterWaitForSingleObject-Methode.

  • Ausführen von asynchronen Aufgaben mit der Task Parallel Library

  • Asynchrones Ausführen von Code mit QueueUserWorkItem

  • Bereitstellen von Aufgabendaten für "QueueUserWorkItem"

  • Verwenden von RegisterWaitForSingleObject

Ausführen von asynchronen Aufgaben mit der Task Parallel Library

Im folgenden Beispiel wird veranschaulicht, wie mit einem Aufruf der TaskFactory.StartNew-Methode ein Task-Objekt erstellt und verwendet wird. Ein Beispiel, in dem die Task<TResult>-Klasse verwendet wird, um einen Wert aus einer asynchronen Aufgabe zurückzugeben, finden Sie unter Gewusst wie: Zurückgeben eines Werts aus einer Aufgabe.

Imports System.Threading
Imports System.Threading.Tasks
Module StartNewDemo

    ' Demonstrated features:
    '   Task ctor()
    '   Task.Factory
    '   Task.Wait()
    '   Task.RunSynchronously()
    ' Expected results:
    '   Task t1 (alpha) is created unstarted.
    '   Task t2 (beta) is created started.
    '   Task t1's (alpha) start is held until after t2 (beta) is started.
    '   Both tasks t1 (alpha) and t2 (beta) are potentially executed on threads other than the main thread on multi-core machines.
    '   Task t3 (gamma) is executed synchronously on the main thread.
    ' Documentation:
    '   https://msdn.microsoft.com/en-us/library/system.threading.tasks.task_members(VS.100).aspx
    Private Sub Main()
        Dim action As Action(Of Object) = Sub(obj As Object)
                                              Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId)
                                          End Sub

        ' Construct an unstarted task
        Dim t1 As New Task(action, "alpha")

        ' Cosntruct a started task
        Dim t2 As Task = Task.Factory.StartNew(action, "beta")

        ' Block the main thread to demonstate that t2 is executing
        t2.Wait()

        ' Launch t1 
        t1.Start()

        Console.WriteLine("t1 has been launched. (Main Thread={0})", Thread.CurrentThread.ManagedThreadId)

        ' Wait for the task to finish.
        ' You may optionally provide a timeout interval or a cancellation token
        ' to mitigate situations when the task takes too long to finish.
        t1.Wait()

        ' Construct an unstarted task
        Dim t3 As New Task(action, "gamma")

        ' Run it synchronously
        t3.RunSynchronously()

        ' Although the task was run synchrounously, it is a good practice to wait for it which observes for 
        ' exceptions potentially thrown by that task.
        t3.Wait()
    End Sub


End Module
using System;
using System.Threading;
using System.Threading.Tasks;

class StartNewDemo
{
    // Demonstrated features:
    //      Task ctor()
    //      Task.Factory
    //      Task.Wait()
    //      Task.RunSynchronously()
    // Expected results:
    //      Task t1 (alpha) is created unstarted.
    //      Task t2 (beta) is created started.
    //      Task t1's (alpha) start is held until after t2 (beta) is started.
    //      Both tasks t1 (alpha) and t2 (beta) are potentially executed on threads other than the main thread on multi-core machines.
    //      Task t3 (gamma) is executed synchronously on the main thread.
    // Documentation:
    //      https://msdn.microsoft.com/en-us/library/system.threading.tasks.task_members(VS.100).aspx
    static void Main()
    {
        Action<object> action = (object obj) =>
        {
            Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);
        };

        // Construct an unstarted task
        Task t1 = new Task(action, "alpha");

        // Cosntruct a started task
        Task t2 = Task.Factory.StartNew(action, "beta");

        // Block the main thread to demonstate that t2 is executing
        t2.Wait();

        // Launch t1 
        t1.Start();

        Console.WriteLine("t1 has been launched. (Main Thread={0})", Thread.CurrentThread.ManagedThreadId);

        // Wait for the task to finish.
        // You may optionally provide a timeout interval or a cancellation token
        // to mitigate situations when the task takes too long to finish.
        t1.Wait();

        // Construct an unstarted task
        Task t3 = new Task(action, "gamma");

        // Run it synchronously
        t3.RunSynchronously();

        // Although the task was run synchrounously, it is a good practice to wait for it which observes for 
        // exceptions potentially thrown by that task.
        t3.Wait();
    }


}

Asynchrones Ausführen von Code mit QueueUserWorkItem

Im folgenden 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.");
    }
}
using namespace System;
using namespace System::Threading;

public ref class Example
{
public:
    static void Main()
    {
        // Queue the task.
        ThreadPool::QueueUserWorkItem(gcnew 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.");
    }
};

int main()
{
    Example::Main();
}

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);
    }
}
using namespace System;
using namespace System::Threading;

// TaskInfo holds state information for a task that will be
// executed by a ThreadPool thread.
public ref 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;
    int Value;

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

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

        // Queue the task and data.
        if (ThreadPool::QueueUserWorkItem(gcnew 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);
    }
};

int main()
{
    Example::Main();
}

Verwenden von 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
        );
    }
}
using namespace System;
using namespace System::Threading;

// TaskInfo contains data that will be passed to the callback
// method.
public ref class TaskInfo
{
public:
    static RegisteredWaitHandle^ Handle = nullptr;
    static String^ OtherInfo = "default";
};

public ref class Example
{
public:
    static void Main()
    {
        // The main thread uses AutoResetEvent to signal the
        // registered wait handle, which executes the callback
        // method.
        AutoResetEvent^ ev = gcnew AutoResetEvent(false);

        TaskInfo^ ti = gcnew 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,
            gcnew 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.
    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 != nullptr)
                ti->Handle->Unregister(nullptr);
        }

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

int main()
{
    Example::Main();
}

Siehe auch

Aufgaben

Gewusst wie: Zurückgeben eines Werts aus einer Aufgabe

Referenz

ThreadPool

Task

Task<TResult>

Konzepte

Task Parallel Library

Task Parallel Library

Threads und Threading

Asynchrone Datei-E/A

Zeitgeber

Weitere Ressourcen

Threadingobjekte und -features

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

September 2010

Veraltete Standardgröße und veraltete Informationen über die Erstellung neuer Threads korrigiert. Beispiel aus Task Parallel Library hinzugefügt.

Korrektur inhaltlicher Fehler.