Compartir a través de


Grupo de subprocesos administrados

La clase ThreadPool proporciona a la aplicación un grupo de subprocesos de trabajo administrados por el sistema, que le permite concentrarse en tareas de aplicación en lugar de en la administración de los subprocesos. Si tiene tareas cortas que requieren el proceso de segundo plano, el grupo de subprocesos administrado es una manera fácil de aprovechar los diversos subprocesos. Por ejemplo, a partir de .NET Framework versión 4 puede crear objetos Task y Task<TResult>, que realizan tareas asincrónicas en subprocesos del grupo de subprocesos.

NotaNota

A partir de .NET Framework versión 2.0 Service Pack 1, el rendimiento del grupo de subprocesos ha mejorado significativamente en tres áreas clave que se identificaron como cuellos de botella en versiones anteriores de .NET Framework: poner tareas en la cola, enviar subprocesos del grupo de subprocesos y enviar subprocesos de finalización de E/S.Para usar esta funcionalidad, el destino de la aplicación debe ser .NET Framework versión 3.5 o posterior.

Para las tareas de segundo plano que interactúan con la interfaz de usuario, la versión 2.0 de .NET Framework también proporciona la clase BackgroundWorker, que se comunica mediante eventos iniciados en el subproceso de la interfaz de usuario.

.NET Framework utiliza subprocesos ThreadPool para muchos fines, incluida la finalización de E/S asincrónica, devolución de llamadas a temporizadores, operaciones de espera registradas, llamadas asincrónicas a métodos utilizando delegados y conexiones de socket System.Net.

Cuándo no utilizar los subprocesos ThreadPool

Hay varios escenarios en los que es adecuado crear y administrar sus propios subprocesos en lugar de utilizar subprocesos ThreadPool:

  • Requiere tener un subproceso de primer plano.

  • Requiere que un subproceso que tenga una prioridad determinada.

  • Hay tareas que hacen que el subproceso se bloquee durante los períodos de tiempo prolongados. El grupo de subprocesos tiene un número máximo de subprocesos, por lo que un número grande de subprocesos ThreadPool bloqueados podría impedir que se iniciaran las tareas.

  • Es necesario colocar los subprocesos en un apartamento de un único subproceso. Todos los subprocesos ThreadPool están en el apartamento multiproceso.

  • Necesita tener una identidad estable asociada al subproceso o dedicar un subproceso a una tarea.

Características de los grupos de subprocesos

Los subprocesos ThreadPool son subprocesos de fondo. Vea Subprocesos de primer y segundo plano. Cada subproceso utiliza el tamaño de pila predeterminado, se ejecuta con la prioridad predeterminada y está en el apartamento multiproceso.

Sólo hay un grupo de subprocesos por cada proceso.

Excepciones en los subprocesos ThreadPool

Las excepciones no controladas producidas en subprocesos ThreadPool finalizan el proceso. Hay tres excepciones a esta regla:

  • En un subproceso ThreadPool se produce una excepción ThreadAbortException porque se llamó a Abort.

  • Se produce una excepción AppDomainUnloadedException en un subproceso ThreadPool, porque se descarga el dominio de aplicación.

  • Common Language Runtime o un proceso de host finaliza el subproceso.

Para obtener más información, vea Excepciones en subprocesos administrados.

NotaNota

En las versiones 1.0 y 1.1 de .NET Framework, Common Language Runtime intercepta silenciosamente las excepciones no controladas en subprocesos ThreadPool.Esto podría dañar el estado de la aplicación y hacer que en el futuro las aplicaciones no respondan, lo que podría ser muy difícil de depurar.

Número máximo de subprocesos ThreadPool

El número de operaciones que pueden situarse en cola del grupo de subprocesos sólo está limitado por la memoria disponible; sin embargo, el grupo de subprocesos limita el número de subprocesos que pueden estar activos simultáneamente en el mismo proceso. A partir de .NET Framework versión 4, el tamaño predeterminado del grupo de subprocesos de un proceso depende de varios factores, como el tamaño del espacio de direcciones virtuales. Un proceso puede llamar al método GetMaxThreads para determinar el número de subprocesos.

Puede controlar el número máximo de subprocesos utilizando los métodos GetMaxThreads y SetMaxThreads.

NotaNota

En las versiones 1.0 y 1.1 de .NET Framework, el tamaño del grupo de subprocesos no se puede establecer desde código administrado.El código que hospeda Common Language Runtime puede establecer el tamaño mediante CorSetMaxThreads, definido en mscoree.h.

Valores mínimos del grupo de subprocesos

El grupo de subprocesos proporciona nuevos subprocesos de trabajo o subprocesos de finalización de E/S a petición hasta que alcanza un mínimo especificado para cada categoría. Puede usar el método GetMinThreads para obtener estos valores mínimos.

NotaNota

Cuando la petición es baja, el número real de subprocesos del grupo de subprocesos puede situarse por debajo de los valores mínimos.

Cuando se alcanza un valor mínimo, el grupo de subprocesos puede crear subprocesos adicionales o esperar hasta que se completan algunas tareas. A partir de .NET Framework 4, el grupo de subprocesos crea y destruye los subprocesos de trabajo para optimizar el rendimiento, que se define como el número de tareas completadas por unidad de tiempo. Es posible que si hay muy pocos subprocesos, no se haga un uso óptimo de los recursos disponibles, mientras que demasiados subprocesos podrían aumentar la contención de recursos.

Nota de precauciónPrecaución

Puede usar el método SetMinThreads para aumentar el número mínimo de subprocesos inactivos.Sin embargo, si estos valores se incrementan innecesariamente, pueden producirse problemas de rendimiento.Si se inician demasiadas tareas al mismo tiempo, puede parecer que todas se ejecutan con lentitud.En la mayoría de los casos, el grupo de subprocesos funcionará mejor con su propio algoritmo de asignación de subprocesos.

Omitir las comprobaciones de seguridad

El grupo de subprocesos también proporciona los métodos ThreadPool.UnsafeQueueUserWorkItem y ThreadPool.UnsafeRegisterWaitForSingleObject. Utilice estos métodos únicamente si está seguro de que pila del llamador no es pertinente para las comprobaciones de seguridad realizadas durante la ejecución de la tarea en cola. QueueUserWorkItem y RegisterWaitForSingleObject capturan la pila del llamador, que se combina en la pila del subproceso del grupo de subprocesos cuando el subproceso empieza a ejecutar una tarea. Si es necesario realizar una comprobación de seguridad, debe comprobarse toda la pila. Aunque la comprobación proporciona seguridad, es a costa del rendimiento.

Utilizar el grupo de subprocesos

A partir de .NET Framework 4, el mecanismo más sencillo para usar el grupo de subprocesos consiste en usar Task Parallel Library. De forma predeterminada, los tipos bibliotecas paralelas, como Task y Task<TResult>, usan los subprocesos del grupo de subprocesos para ejecutar tareas. También puede usar el grupo de subprocesos llamando a ThreadPool.QueueUserWorkItem desde el código administrado (o a CorQueueUserWorkItem desde el código no administrado) y pasando un delegado WaitCallback que representa el método que realiza la tarea. Otra forma de usar el grupo de subprocesos consiste en poner en cola los elementos de trabajo que están relacionados con una operación de espera usando el método ThreadPool.RegisterWaitForSingleObject y pasando WaitHandle que, cuando se señala o agota el tiempo de espera, llama al método representado por el delegado WaitOrTimerCallback. Los subprocesos del grupo de subprocesos se usan para invocar métodos de devolución de llamada.

Ejemplos de ThreadPool

En los ejemplos de código de esta sección se muestra el grupo de subprocesos usando la clase Task, el método ThreadPool.QueueUserWorkItem y el método ThreadPool.RegisterWaitForSingleObject.

  • Ejecutar tareas asincrónicas con la biblioteca TPL

  • Ejecutar código asincrónicamente con QueueUserWorkItem

  • Proporcionar datos de tareas para QueueUserWorkItem

  • Usar RegisterWaitForSingleObject

Ejecutar tareas asincrónicas con la biblioteca TPL

En el ejemplo siguiente se muestra cómo se crea y se usa un objeto Task llamando al método TaskFactory.StartNew. Para obtener un ejemplo en el que se usa la clase Task<TResult> para devolver un valor de una tarea asincrónica, vea Cómo: Devolver un valor de una tarea.

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


}

Ejecutar código asincrónicamente con QueueUserWorkItem

En el ejemplo siguiente se pone en cola una tarea muy sencilla, representada por el método ThreadProc, usando el método 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();
}

Proporcionar datos de tareas para QueueUserWorkItem

En el siguiente ejemplo de código se utiliza el método QueueUserWorkItem para poner en cola una tarea y proporcionar los datos de esa tarea.

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

Usar RegisterWaitForSingleObject

En el ejemplo siguiente se muestran diversas características del subprocesamiento.

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

Vea también

Tareas

Cómo: Devolver un valor de una tarea

Referencia

ThreadPool

Task

Task<TResult>

Conceptos

Task Parallel Library

Task Parallel Library

Subprocesos y subprocesamiento

E/S de archivos asincrónica

temporizadores

Otros recursos

Objetos y características de subprocesos

Historial de cambios

Fecha

Historial

Motivo

Septiembre de 2010

Se ha corregido el tamaño predeterminado obsoleto y la información obsoleta sobre la creación de nuevos subprocesos. Se ha agregado un ejemplo de Task Parallel Library.

Corrección de errores de contenido.