Condividi tramite


Pool di thread gestiti

La classe ThreadPool fornisce all'applicazione un pool di thread di lavoro gestiti dal sistema, che consentono di concentrarsi sulle attività dell'applicazione anziché sulla gestione dei thread. Se si dispone di attività brevi per cui è necessaria l'elaborazione in background, il pool di thread gestiti rappresenta la tecnica più semplice per utilizzare più thread. A partire da .NET Framework versione 4 è ad esempio possibile creare oggetti Task<TResult> e Task, che consentono di eseguire attività asincrone sui thread del pool di thread.

NotaNota

A partire da .NET Framework 2.0 Service Pack 1, la trasmissione dei dati del pool di thread è migliorata significativamente in tre aree principali considerate come colli di bottiglia nelle versioni precedenti di .NET Framework: mettendo in coda attività, inviando thread del pool di thread e inviando thread di completamento di I/O.Per utilizzare questa funzionalità, l'applicazione deve essere destinata a .NET Framework versione 3.5 o versioni successive.

Per le attività in background che interagiscono con l'interfaccia utente, .NET Framework versione 2.0 fornisce anche la classe BackgroundWorker, che comunica utilizzando gli eventi che si sono verificati nel thread dell'interfaccia utente.

.NET Framework utilizza i thread del pool di thread per diversi scopi, ad esempio il completamento I/O asincrono, i callback del timer, le operazioni di attesa registrate, le chiamate ai metodi asincroni effettuate tramite delegati e le connessioni socket a System.Net.

Situazioni in cui non è opportuno utilizzare i thread del pool di thread

Esistono diversi scenari in cui è preferibile creare e gestire thread personalizzati anziché utilizzare quelli del pool, in particolare quando:

  • È necessario un thread in primo piano.

  • È necessario che un thread abbia una determinata priorità.

  • Sono presenti attività che bloccano il thread per periodi di tempo prolungati. È previsto un numero massimo di thread del pool e, pertanto, è possibile che il blocco di numerosi thread del pool impedisca l'avvio delle attività.

  • È necessario inserire i thread in un apartment a thread singolo. Tutti i thread di ThreadPool sono inclusi in apartment con multithreading.

  • È necessario disporre di un'identità stabile associata al thread oppure utilizzare un thread esclusivamente per un'attività.

Caratteristiche del pool di thread

I thread del pool sono thread in background. Per informazioni, vedere Thread in primo piano e in background. Ogni thread utilizza una dimensione di stack predefinita, viene eseguito alla priorità predefinita e si trova nell'apartment con multithreading.

È disponibile un solo pool di thread per processo.

Eccezioni nei thread del pool di thread

Le eccezioni non gestite nei thread del pool determinano l'interruzione del processo. Esistono tre eccezioni a questa regola:

  • Viene generata un'eccezione ThreadAbortException in un thread del pool per effetto di una chiamata a Abort.

  • Viene generata un'eccezione AppDomainUnloadedException in un thread del pool perché è in corso lo scaricamento del dominio applicazione.

  • Il thread viene interrotto da Common Language Runtime o da un processo host.

Per ulteriori informazioni, vedere Eccezioni in thread gestiti.

NotaNota

In .NET Framework versioni 1.0 e 1.1 Common Language Runtime intercetta automaticamente le eccezioni non gestite nei thread del pool.Questo comportamento può danneggiare lo stato dell'applicazione ed eventualmente determinare il blocco delle applicazioni, rendendo particolarmente difficile il debug.

Numero massimo di thread del pool di thread

Il numero di operazioni che possono essere accodate al pool di thread è limitato solo dalla disponibilità di memoria. Tuttavia, il pool di thread limita il numero di thread che possono essere attivi simultaneamente nel processo. A partire da .NET Framework versione 4, le dimensioni predefinite del pool di thread per un processo dipendono da diversi fattori, ad esempio le dimensioni dello spazio degli indirizzi virtuali. Un processo può chiamare il metodo GetMaxThreads per determinare il numero di thread.

È possibile controllare il numero massimo di thread utilizzando i metodi GetMaxThreads e SetMaxThreads.

NotaNota

In .NET Framework versioni 1.0 e 1.1 non è possibile impostare la dimensione del pool di thread dal codice gestito.Il codice in cui è incluso Common Language Runtime può impostare la dimensione utilizzando CorSetMaxThreads, definito in mscoree.h.

Valori minimi del pool di thread

Il pool di thread fornisce nuovi thread di lavoro o thread di completamento di I/O su richiesta fino al raggiungimento di un valore minimo specificato per ogni categoria. È possibile utilizzare il metodo GetMinThreads per ottenere questi valori minimi.

NotaNota

Quando la richiesta è bassa, il numero effettivo di thread del pool di thread può scendere sotto i valori minimi.

Quando viene raggiunto un minimo, il pool di thread può creare thread aggiuntivi o attendere il completamento di alcune attività. A partire da .NET Framework 4, il pool di thread crea ed elimina in modo permanente i thread di lavoro, per ottimizzare la velocità effettiva, definita come numero di attività completate per unità di tempo. Un numero troppo basso di thread potrebbe non consentire un utilizzo ottimale delle risorse disponibili, mentre un numero troppo alto di thread potrebbe comportare un aumento dei conflitti di risorse.

Nota di avvisoAttenzione

È possibile utilizzare il metodo SetMinThreads per aumentare il numero minimo di thread inattivi.Se tuttavia si aumentano inutilmente questi valori, potrebbero verificarsi problemi di prestazioni.Se si avviano troppe attività contemporaneamente, potrebbero risultare tutte lente.Nella maggior parte dei casi, le prestazioni del pool di thread risultano migliori in presenza di un algoritmo per l'allocazione dei thread.

Ignorare i controlli di sicurezza

Il pool di thread fornisce anche i metodi ThreadPool.UnsafeQueueUserWorkItem e ThreadPool.UnsafeRegisterWaitForSingleObject. Utilizzare questi metodi solo quando si è sicuri che lo stack del chiamante sia irrilevante ai fini dei controlli di sicurezza eseguiti durante l'esecuzione dell'attività in coda. Sia QueueUserWorkItem che RegisterWaitForSingleObject acquisiscono lo stack del chiamante, il quale viene unito allo stack del thread del pool di thread quando il thread avvia l'esecuzione di un'attività. Se è richiesto un controllo di sicurezza, esso deve essere eseguito in tutto lo stack. Sebbene tale controllo offra garanzie di sicurezza, ha anche dei costi in termini di prestazioni.

Utilizzo del pool di thread

A partire da .NET Framework 4, il modo più semplice per utilizzare il pool di thread consiste nell'utilizzare la Task Parallel Library. Per impostazione predefinita, i tipi di librerie TPL (Task Parallel Library), come Task e Task<TResult>, utilizzano thread del pool di thread per eseguire le attività. È anche possibile utilizzare il pool di thread chiamando ThreadPool.QueueUserWorkItem dal codice gestito, o CorQueueUserWorkItem dal codice non gestito, e passando un delegato WaitCallback che rappresenta il metodo da cui viene eseguita l'attività. È inoltre possibile utilizzare un pool di thread accodando elementi di lavoro correlati a un'operazione di attesa utilizzando il metodo ThreadPool.RegisterWaitForSingleObject e passando un oggetto WaitHandle che, alla ricezione di un segnale o alla scadenza, chiama il metodo rappresentato dal delegato WaitOrTimerCallback. I thread del pool di thread vengono utilizzati per richiamare metodi di callback.

Esempi di ThreadPool

Negli esempi di codice in questa sezione viene illustrato il pool di thread utilizzando la classe Task, il metodo ThreadPool.QueueUserWorkItem e il metodo ThreadPool.RegisterWaitForSingleObject.

  • Esecuzione di attività asincrone con la libreria TPL (Task Parallel Library)

  • Esecuzione di codice in modo asincrono con QueueUserWorkItem

  • Fornire dati attività per QueueUserWorkItem

  • Utilizzo di RegisterWaitForSingleObject

Esecuzione di attività asincrone con la libreria TPL (Task Parallel Library)

Nell'esempio seguente viene illustrato come creare e utilizzare un oggetto Task chiamando il metodo TaskFactory.StartNew. Per un esempio in cui viene utilizzata la classe Task<TResult> per restituire un valore da un'attività asincrona, vedere Procedura: restituire un valore da un'attività.

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


}

Esecuzione di codice in modo asincrono con QueueUserWorkItem

Nell'esempio seguente viene accodata un'attività molto semplice, rappresentata dal metodo ThreadProc, utilizzando il metodo QueueUserWorkItem.

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

Fornire dati attività per QueueUserWorkItem

Nell'esempio di codice riportato di seguito viene utilizzato il metodo QueueUserWorkItem per accodare un'attività e fornire i relativi dati.

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

Utilizzo di RegisterWaitForSingleObject

Nell'esempio che segue vengono illustrate diverse funzionalità del threading.

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

Vedere anche

Attività

Procedura: restituire un valore da un'attività

Riferimenti

ThreadPool

Task

Task<TResult>

Concetti

Task Parallel Library

Task Parallel Library

Thread e threading

I/O di file asincrono

timer

Altre risorse

Oggetti e funzionalità del threading

Cronologia delle modifiche

Data

Cronologia

Motivo

Settembre 2010

Correzione delle dimensioni predefinite non aggiornate e di informazioni non aggiornate sulla creazione di nuovi thread. Aggiunta di un esempio da Task Parallel Library.

Correzione di bug nel contenuto.