Grupo de subprocesos administradosThe Managed Thread Pool

La clase ThreadPool proporciona a la aplicación un grupo de subprocesos de trabajo administrados por el sistema, que le permite concentrarse en las tareas de la aplicación en lugar de en la administración de los subprocesos.The ThreadPool class provides your application with a pool of worker threads that are managed by the system, allowing you to concentrate on application tasks rather than thread management. Si tiene tareas cortas que requieran procesamiento en segundo plano, el grupo de subprocesos administrados permite aprovechar fácilmente las ventajas de varios subprocesos.If you have short tasks that require background processing, the managed thread pool is an easy way to take advantage of multiple threads. Por ejemplo, a partir de .NET Framework 4.NET Framework 4, puede crear objetos Task y Task<TResult>, que realizan tareas asincrónicas en los subprocesos del grupo de subprocesos.For example, beginning with the .NET Framework 4.NET Framework 4 you can create Task and Task<TResult> objects, which perform asynchronous tasks on thread pool threads.

Nota

A partir de .NET Framework 2.0 Service Pack 1.NET Framework 2.0 Service Pack 1, mejora considerablemente el rendimiento del grupo de subprocesos en tres áreas clave que se identificaron como cuellos de botella en las versiones anteriores de .NET Framework.NET Framework: poner tareas en cola, enviar subprocesos del grupo de subprocesos y enviar subprocesos de finalización de E/S.Starting with the .NET Framework 2.0 Service Pack 1.NET Framework 2.0 Service Pack 1, the throughput of the thread pool is significantly improved in three key areas that were identified as bottlenecks in previous releases of the .NET Framework.NET Framework: queuing tasks, dispatching thread pool threads, and dispatching I/O completion threads. Para utilizar esta funcionalidad, la aplicación debe utilizar .NET Framework 3,5.NET Framework 3.5 o posterior como destino.To use this functionality, your application should target the .NET Framework 3,5.NET Framework 3.5 or later.

Para las tareas en 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 generados en el subproceso de la interfaz de usuario.For background tasks that interact with the user interface, the .NET Framework version 2.0 also provides the BackgroundWorker class, which communicates using events raised on the user interface thread.

.NET Framework utiliza los subprocesos del grupo de subprocesos para muchos fines, incluida la finalización de E/S asincrónica, las devoluciones de llamada del temporizador, las operaciones de espera registradas, las llamadas de métodos asincrónicos mediante delegados y las conexiones de socket System.Net.The .NET Framework uses thread pool threads for many purposes, including asynchronous I/O completion, timer callbacks, registered wait operations, asynchronous method calls using delegates, and System.Net socket connections.

Cuándo no usar subprocesos del grupo de subprocesosWhen Not to Use Thread Pool Threads

Hay varios escenarios en los que es adecuado crear y administrar sus propios subprocesos en lugar de utilizar subprocesos del grupo de subprocesos:There are several scenarios in which it is appropriate to create and manage your own threads instead of using thread pool threads:

  • Necesita un subproceso en primer plano.You require a foreground thread.

  • Necesita que un subproceso tenga una prioridad determinada.You require a thread to have a particular priority.

  • Tiene tareas que hacen que el subproceso se bloquee durante largos períodos de tiempo.You have tasks that cause the thread to block for long periods of time. El grupo de subprocesos tiene un número máximo de subprocesos, por lo que si hay un gran número de subprocesos del grupo de subprocesos bloqueados, esto puede impedir que se inicien las tareas.The thread pool has a maximum number of threads, so a large number of blocked thread pool threads might prevent tasks from starting.

  • Debe colocar los subprocesos en un contenedor uniproceso.You need to place threads into a single-threaded apartment. Todos los subprocesos ThreadPool están en el contenedor multiproceso.All ThreadPool threads are in the multithreaded apartment.

  • Debe tener una identidad estable asociada al subproceso o dedicar un subproceso a una tarea.You need to have a stable identity associated with the thread, or to dedicate a thread to a task.

Características del grupo de subprocesosThread Pool Characteristics

Los subprocesos del grupo de subprocesos son subprocesos en segundo plano.Thread pool threads are background threads. Consulte Subprocesos de primer y segundo plano.See Foreground and Background Threads. Cada subproceso utiliza el tamaño de pila predeterminado, se ejecuta con la prioridad predeterminada y está en el contenedor multiproceso.Each thread uses the default stack size, runs at the default priority, and is in the multithreaded apartment.

Hay solo un grupo de subprocesos por cada proceso.There is only one thread pool per process.

Excepciones en los subprocesos del grupo de subprocesosExceptions in Thread Pool Threads

Las excepciones no controladas en los subprocesos del grupo de subprocesos finalizan el proceso.Unhandled exceptions on thread pool threads terminate the process. Hay tres excepciones de esta regla:There are three exceptions to this rule:

  • Se genera un ThreadAbortException en un subproceso del grupo de subprocesos, porque se llamó a Abort.A ThreadAbortException is thrown in a thread pool thread, because Abort was called.

  • Se genera un AppDomainUnloadedException en un subproceso del grupo de subprocesos, porque se está descargando el dominio de aplicación.An AppDomainUnloadedException is thrown in a thread pool thread, because the application domain is being unloaded.

  • Common Language Runtime o un proceso de host finaliza el subproceso.The common language runtime or a host process terminates the thread.

Para más información, consulte Excepciones en subprocesos administrados.For more information, see Exceptions in Managed Threads.

Nota

En las versiones 1.0 y 1.1 de .NET Framework, Common Language Runtime intercepta silenciosamente las excepciones no controladas en los subprocesos del grupo de subprocesos.In the .NET Framework versions 1.0 and 1.1, the common language runtime silently traps unhandled exceptions in thread pool threads. Esto puede dañar el estado de la aplicación y puede acabar haciendo que las aplicaciones no respondan, lo cual puede ser muy difícil de depurar.This might corrupt application state and eventually cause applications to hang, which might be very difficult to debug.

Número máximo de subprocesos del grupo de subprocesosMaximum Number of Thread Pool Threads

El número de operaciones que pueden ponerse en la cola del grupo de subprocesos está limitado solamente por la memoria disponible. Sin embargo, el grupo de subprocesos limita el número de subprocesos que pueden estar activos en el proceso de forma simultánea.The number of operations that can be queued to the thread pool is limited only by available memory; however, the thread pool limits the number of threads that can be active in the process simultaneously. A partir de .NET Framework 4.NET Framework 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.Beginning with the .NET Framework 4.NET Framework 4, the default size of the thread pool for a process depends on several factors, such as the size of the virtual address space. Un proceso puede llamar al método GetMaxThreads para determinar el número de subprocesos.A process can call the GetMaxThreads method to determine the number of threads.

El número máximo de subprocesos se puede controlar con los métodos GetMaxThreads y SetMaxThreads.You can control the maximum number of threads by using the GetMaxThreads and SetMaxThreads methods.

Nota

En las versiones 1.0 y 1.1 de .NET Framework, el tamaño del grupo de subprocesos no se puede establecer desde el código administrado.In the .NET Framework versions 1.0 and 1.1, the size of the thread pool cannot be set from managed code. El código que hospeda Common Language Runtime puede establecer el tamaño con CorSetMaxThreads, definido en mscoree.h.Code that hosts the common language runtime can set the size using CorSetMaxThreads, defined in mscoree.h.

Mínimos del grupo de subprocesosThread Pool Minimums

El grupo de subprocesos ofrece nuevos subprocesos de trabajo o subprocesos de finalización de E/S a petición hasta que llega a un mínimo especificado para cada categoría.The thread pool provides new worker threads or I/O completion threads on demand until it reaches a specified minimum for each category. Puede utilizar el método GetMinThreads para obtener estos valores mínimos.You can use the GetMinThreads method to obtain these minimum values.

Nota

Cuando la demanda es baja, el número real de subprocesos del grupo de subprocesos puede descender por debajo de los valores mínimos.When demand is low, the actual number of thread pool threads can fall below the minimum values.

Cuando se alcanza el mínimo, el grupo de subprocesos puede crear subprocesos adicionales o esperar hasta que se completen algunas tareas.When a minimum is reached, the thread pool can create additional threads or wait until some tasks complete. A partir de .NET Framework 4.NET Framework 4, el grupo de subprocesos crea y destruye subprocesos de trabajo para optimizar el rendimiento, definido como el número de tareas que se completan por unidad de tiempo.Beginning with the .NET Framework 4.NET Framework 4, the thread pool creates and destroys worker threads in order to optimize throughput, which is defined as the number of tasks that complete per unit of time. Si hay demasiados pocos subprocesos, puede que los recursos disponibles no se usen de manera óptima, mientras que si hay demasiados subprocesos, puede aumentar la contención de recursos.Too few threads might not make optimal use of available resources, whereas too many threads could increase resource contention.

Precaución

Puede utilizar el método SetMinThreads para aumentar el número mínimo de subprocesos inactivos.You can use the SetMinThreads method to increase the minimum number of idle threads. Sin embargo, aumentar innecesariamente estos valores puede causar problemas de rendimiento.However, unnecessarily increasing these values can cause performance problems. Si se inician demasiadas tareas al mismo tiempo, puede que todas ellas parezcan funcionar con lentitud.If too many tasks start at the same time, all of them might appear to be slow. En la mayoría de los casos, el grupo de subprocesos funciona mejor con su propio algoritmo de asignación de subprocesos.In most cases the thread pool will perform better with its own algorithm for allocating threads.

Omisión de comprobaciones de seguridadSkipping Security Checks

El grupo de subprocesos también proporciona los métodos ThreadPool.UnsafeQueueUserWorkItem y ThreadPool.UnsafeRegisterWaitForSingleObject.The thread pool also provides the ThreadPool.UnsafeQueueUserWorkItem and ThreadPool.UnsafeRegisterWaitForSingleObject methods. Utilice estos métodos solamente cuando tenga la seguridad de que la pila del llamador es irrelevante para las comprobaciones de seguridad que se realizan durante la ejecución de la tarea en cola.Use these methods only when you are certain that the caller's stack is irrelevant to any security checks performed during the execution of the queued task. 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.QueueUserWorkItem and RegisterWaitForSingleObject both capture the caller's stack, which is merged into the stack of the thread pool thread when the thread begins to execute a task. Si es necesaria una comprobación de seguridad, debe comprobarse toda la pila.If a security check is required, the entire stack must be checked. La comprobación proporciona seguridad, pero también supone un coste para el rendimiento.Although the check provides safety, it also has a performance cost.

Uso del grupo de subprocesosUsing the Thread Pool

A partir de .NET Framework 4.NET Framework 4, la manera más fácil de usar el grupo de subprocesos es utilizar la Biblioteca TPL.Beginning with the .NET Framework 4.NET Framework 4, the easiest way to use the thread pool is to use the Task Parallel Library (TPL). De forma predeterminada, los tipos de biblioteca de procesamiento paralelo, como Task y Task<TResult>, utilizan subprocesos del grupo de subprocesos para ejecutar tareas.By default, parallel library types like Task and Task<TResult> use thread pool threads to run tasks. También puede utilizar el grupo de subprocesos llamando a ThreadPool.QueueUserWorkItem desde código administrado (o CorQueueUserWorkItem desde código no administrado) y pasando un delegado WaitCallback que represente al método que realiza la tarea.You can also use the thread pool by calling ThreadPool.QueueUserWorkItem from managed code (or CorQueueUserWorkItem from unmanaged code) and passing a WaitCallback delegate representing the method that performs the task. Otra forma de usar el grupo de subprocesos es poner en cola los elementos de trabajo que están relacionados con una operación de espera mediante el método ThreadPool.RegisterWaitForSingleObject y pasar un WaitHandle que, cuando se señala o cuando se agota el tiempo de espera, llame al método representado por el delegado WaitOrTimerCallback.Another way to use the thread pool is to queue work items that are related to a wait operation by using the ThreadPool.RegisterWaitForSingleObject method and passing a WaitHandle that, when signaled or when timed out, calls the method represented by the WaitOrTimerCallback delegate. Los subprocesos del grupo de subprocesos se usan para invocar métodos de devolución de llamada.Thread pool threads are used to invoke callback methods.

Ejemplos de ThreadPoolThreadPool Examples

Los ejemplos de código de esta sección muestran el grupo de subprocesos con la clase Task, el método ThreadPool.QueueUserWorkItem y el método ThreadPool.RegisterWaitForSingleObject.The code examples in this section demonstrate the thread pool by using the Task class, the ThreadPool.QueueUserWorkItem method, and the ThreadPool.RegisterWaitForSingleObject method.

Ejecutar tareas asincrónicas con la biblioteca TPLExecuting Asynchronous Tasks with the Task Parallel Library

En el siguiente ejemplo, se muestra cómo crear y usar un objeto Task para llamar al método TaskFactory.StartNew.The following example shows how to create and use a Task object by calling the TaskFactory.StartNew method. Para ver un ejemplo que utiliza la clase Task<TResult> con el fin de devolver un valor de una tarea asincrónica, consulte Cómo: Devolver un valor de una tarea.For an example that uses the Task<TResult> class to return a value from an asynchronous task, see How to: Return a Value from a Task.

using System;
using System.Threading;
using System.Threading.Tasks;

class Example
{
    static void Main()
    {
        Action<object> action = (object obj) =>
                                {
                                   Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                   Task.CurrentId, obj,
                                   Thread.CurrentThread.ManagedThreadId);
                                };

        // Create a task but do not start it.
        Task t1 = new Task(action, "alpha");

        // Construct 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.
        t1.Wait();

        // Construct a started task using Task.Run.
        String taskData = "delta";
        Task t3 = Task.Run( () => {Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                                     Task.CurrentId, taskData,
                                                      Thread.CurrentThread.ManagedThreadId);
                                   });
        // Wait for the task to finish.
        t3.Wait();

        // Construct an unstarted task
        Task t4 = new Task(action, "gamma");
        // Run it synchronously
        t4.RunSynchronously();
        // Although the task was run synchronously, it is a good practice
        // to wait for it in the event exceptions were thrown by the task.
        t4.Wait();
    }
}
// The example displays output like the following:
//       Task=1, obj=beta, Thread=3
//       t1 has been launched. (Main Thread=1)
//       Task=2, obj=alpha, Thread=4
//       Task=3, obj=delta, Thread=3
//       Task=4, obj=gamma, Thread=1
Imports System.Threading
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim action As Action(Of Object) = 
              Sub(obj As Object)
                 Console.WriteLine("Task={0}, obj={1}, Thread={2}", 
                 Task.CurrentId, obj,
                 Thread.CurrentThread.ManagedThreadId)
              End Sub

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

        ' Construct 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.
        t1.Wait()

        ' Construct a started task using Task.Run.
        Dim taskData As String = "delta"
        Dim t3 As Task = Task.Run(Sub()
                                     Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                     Task.CurrentId, taskData,
                                     Thread.CurrentThread.ManagedThreadId)
                                  End Sub)
        ' Wait for the task to finish.
        t3.Wait()
        
        ' Construct an unstarted task
        Dim t4 As New Task(action, "gamma")
        ' Run it synchronously
        t4.RunSynchronously()
        ' Although the task was run synchronously, it is a good practice
        ' to wait for it in the event exceptions were thrown by the task.
        t4.Wait()
    End Sub
End Module
' The example displays output like the following:
'       Task=1, obj=beta, Thread=3
'       t1 has been launched. (Main Thread=1)
'       Task=2, obj=alpha, Thread=3
'       Task=3, obj=delta, Thread=3
'       Task=4, obj=gamma, Thread=1

Ejecutar código de forma asincrónica con QueueUserWorkItemExecuting Code Asynchronously with QueueUserWorkItem

En el ejemplo siguiente, se pone en cola una tarea muy sencilla, representada por el método ThreadProc, con el método QueueUserWorkItem.The following example queues a very simple task, represented by the ThreadProc method, using the QueueUserWorkItem method.

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

Proporcionar datos de tareas para QueueUserWorkItemSupplying Task Data for QueueUserWorkItem

El siguiente ejemplo de código utiliza el método QueueUserWorkItem para poner en cola una tarea y proporcionar los datos de la tarea.The following code example uses the QueueUserWorkItem method to queue a task and supply the data for the task.

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

Uso de RegisterWaitForSingleObjectUsing RegisterWaitForSingleObject

En el ejemplo siguiente se muestran diversas características de subprocesamiento:The following example demonstrates several threading features.

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

Vea tambiénSee Also

ThreadPool
Task
Task<TResult>
Biblioteca TPLTask Parallel Library (TPL)
Biblioteca TPLTask Parallel Library (TPL)
Devolver un valor a partir de una tareaHow to: Return a Value from a Task
Objetos y características de subprocesosThreading Objects and Features
Subprocesos y subprocesamientoThreads and Threading
Asynchronous File I/O (E/S de archivos asincrónica)Asynchronous File I/O
Timers (Temporizadores)Timers