다음을 통해 공유


관리되는 스레드 풀

업데이트: 2010년 9월

ThreadPool 클래스를 사용하면 시스템에서 관리하는 작업자 스레드 풀이 응용 프로그램에 제공되므로 사용자는 스레드 관리가 아닌 응용 프로그램 작업에 집중할 수 있습니다. 백그라운드 프로세스가 필요한 짧은 작업이 있는 경우 관리되는 스레드 풀을 사용하여 다중 스레드를 활용할 수 있습니다. 예를 들어 .NET Framework 버전 4부터는 스레드 풀 스레드에서 비동기 작업을 수행하는 TaskTask<TResult> 개체를 만들 수 있습니다.

참고참고

.NET Framework 버전 2.0 서비스 팩 1부터는 이전 릴리스의 .NET Framework에서 병목 현상이 발생하는 것으로 파악된 세 개의 주요 영역인 작업을 큐에 대기시키기, 스레드 풀 스레드 디스패치 및 I/O 완료 스레드 디스패치에서 스레드 풀의 처리량이 크게 향상되었습니다.이 기능을 사용하려면 응용 프로그램이 .NET Framework 버전 3.5 이상을 대상으로 해야 합니다.

사용자 인터페이스와 상호 작용하는 백그라운드 작업의 경우 .NET Framework 버전 2.0에서는 사용자 인터페이스 스레드에서 발생되는 이벤트를 사용하여 통신하는 BackgroundWorker 클래스도 제공합니다.

.NET Framework에서는 비동기 I/O 완료, 타이머 콜백, 등록된 대기 작업, 대리자를 사용한 비동기 메서드 호출 및 System.Net 소켓 연결을 포함한 여러 가지 목적으로 스레드 풀 스레드를 사용합니다.

스레드 풀 스레드를 사용하지 않는 경우

스레드 풀 스레드를 사용하는 대신 사용자 고유의 스레드를 만들고 관리하는 것이 적합한 몇 가지 시나리오는 다음과 같습니다.

  • 포그라운드 스레드가 필요한 경우

  • 스레드에 특정 우선 순위를 지정해야 하는 경우

  • 스레드를 오랫동안 차단하는 작업이 있는 경우 스레드 풀에 최대 스레드 수가 있으므로 다수의 차단된 스레드 풀 스레드가 작업의 시작을 방해할 수 있습니다.

  • 단일 스레드 아파트에 스레드를 배치해야 하는 경우 모든 ThreadPool 스레드는 다중 스레드 아파트에 있습니다.

  • 스레드에 연결된 안정적인 ID가 있어야 하거나 하나의 스레드를 하나의 작업에 전용으로 사용해야 하는 경우

스레드 풀 특징

스레드 풀 스레드는 백그라운드 스레드입니다. 포그라운드 및 백그라운드 스레드를 참조하십시오. 각 스레드는 기본 스택 크기를 사용하고 기본 우선 순위에 따라 실행되며 다중 스레드 아파트에 있습니다.

프로세스당 스레드 풀이 하나만 있습니다.

스레드 풀 스레드의 예외 사항

스레드 풀 스레드의 처리되지 않은 예외로 인해 프로세스가 종료됩니다. 다음은 이 규칙의 세 가지 예외 사항입니다.

  • Abort가 취소되었으므로 스레드 풀 스레드에서 ThreadAbortException이 throw됩니다.

  • 응용 프로그램 도메인이 언로드 중이므로 스레드 풀 스레드에서 AppDomainUnloadedException이 throw됩니다.

  • 공용 언어 런타임 또는 호스트 프로세스에서 스레드를 종료합니다.

자세한 내용은 관리되는 스레드의 예외를 참조하십시오.

참고참고

.NET Framework 버전 1.0 및 1.1에서 공용 언어 런타임은 스레드 풀 스레드에서 처리되지 않은 예외를 포착합니다.이렇게 하면 응용 프로그램 상태가 손상되어 결국 응용 프로그램이 중단될 수 있고, 이에 따라 디버깅이 어려워질 수 있습니다.

최대 스레드 풀 스레드 수

스레드 풀의 큐에 들어갈 수 있는 작업 수는 사용할 수 있는 메모리에 의해서만 제한됩니다. 그러나 스레드 풀이 동시에 프로세스에서 활성화될 수 있는 스레드 수를 제한합니다. .NET Framework 버전 4부터는 프로세스에 대한 스레드 풀의 기본 크기가 가상 주소 공간 크기와 같은 여러 요소에 따라 달라집니다. 프로세스는 스레드 수를 확인하기 위해 GetMaxThreads 메서드를 호출할 수 있습니다.

GetMaxThreadsSetMaxThreads 메서드를 사용하여 최대 스레드 수를 제어할 수 있습니다.

참고참고

.NET Framework 버전 1.0 및 1.1에서는 관리 코드에서 스레드 풀의 크기를 설정할 수 없습니다.공용 언어 런타임을 호스팅하는 코드는 mscoree.h에 정의되어 있는 CorSetMaxThreads를 사용하여 크기를 설정할 수 있습니다.

스레드 풀 최소값

스레드 풀은 각 범주에 대해 지정된 최소값에 도달할 때까지 요청 시 새 작업자 스레드 또는 I/O 완료 스레드를 만듭니다. 이러한 최소값은 GetMinThreads 메서드를 사용하여 가져올 수 있습니다.

참고참고

요구 수준이 낮은 경우 실제 스레드 풀 스레드 수는 최소값보다 낮을 수 있습니다.

최소값에 도달하면 스레드 풀이 추가 스레드를 만들거나 일부 작업이 완료될 때까지 기다릴 수 있습니다. .NET Framework 4부터는 스레드 풀이 시간 단위당 완료되는 작업 수로 정의되는 처리량을 최적화하기 위해 작업자 스레드를 만들고 삭제합니다. 스레드가 너무 적으면 사용 가능한 리소스의 사용 효율성이 낮아질 수 있고, 반대로 스레드가 너무 많으면 리소스 경합이 늘어날 수 있습니다.

주의 정보주의

SetMinThreads 메서드를 사용하여 최소 유휴 스레드 수를 늘릴 수 있습니다.하지만 이러한 값을 불필요하게 늘릴 경우 성능 문제가 발생할 수 있습니다.동시에 너무 많은 작업을 시작하면 모든 작업이 느린 것처럼 나타날 수 있습니다.대부분의 경우 스레드 풀은 스레드 할당을 위해 자체 알고리즘을 사용할 때 더 나은 성능을 제공합니다.

보안 검사 건너뛰기

스레드 풀은 ThreadPool.UnsafeQueueUserWorkItemThreadPool.UnsafeRegisterWaitForSingleObject 메서드를 제공합니다. 이러한 메서드는 대기 중인 작업을 실행하는 동안 수행된 보안 검사와 호출자의 스택이 무관한 경우에만 사용합니다. QueueUserWorkItemRegisterWaitForSingleObject에서 모두 호출자의 스택을 캡처하며, 이것은 스레드에서 작업 실행을 시작하면 스레드 풀 스레드의 스택에 병합됩니다. 보안 검사가 필요하면 전체 스택을 검사해야 하는데, 안전성은 제공되지만 성능이 저하됩니다.

스레드 풀 사용

.NET Framework 4부터 스레드 풀을 사용하는 가장 쉬운 방법은 작업 병렬 라이브러리를 사용하는 것입니다. 기본적으로 TaskTask<TResult>와 같은 병렬 라이브러리 형식에는 작업 실행을 위해 스레드 풀 스레드가 사용됩니다. 또한 관리 코드에서 ThreadPool.QueueUserWorkItem을 호출하거나 비관리 코드에서 CorQueueUserWorkItem을 호출하고 작업을 수행하는 메서드를 나타내는 WaitCallback 대리자를 전달하여 스레드 풀을 사용할 수 있습니다. 스레드 풀을 사용하는 또 다른 방법으로 ThreadPool.RegisterWaitForSingleObject 메서드를 사용하고 신호를 받거나 시간이 초과되었을 때 WaitOrTimerCallback 대리자에서 나타내는 메서드를 호출하는 WaitHandle을 전달하여 대기 작업에 관련된 작업 항목을 대기시킬 수도 있습니다. 스레드 풀 스레드는 콜백 메서드를 호출하는 데 사용됩니다.

ThreadPool 예제

이 단원의 코드 예제에서는 Task 클래스, ThreadPool.QueueUserWorkItem 메서드 및 ThreadPool.RegisterWaitForSingleObject 메서드를 사용하여 스레드 풀을 보여 줍니다.

  • 작업 병렬 라이브러리를 사용하여 비동기 작업 실행

  • QueueUserWorkItem을 사용하여 비동기 방식으로 코드 실행

  • QueueUserWorkItem에 작업 데이터 제공

  • RegisterWaitForSingleObject 사용

작업 병렬 라이브러리를 사용하여 비동기 작업 실행

다음 예제에서는 TaskFactory.StartNew 메서드를 호출하여 Task 개체를 만들고 사용하는 방법을 보여 줍니다. Task<TResult> 클래스를 사용하여 비동기 작업으로부터 값을 반환하는 예를 보려면 방법: 작업에서 값 반환을 참조하십시오.

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


}

QueueUserWorkItem을 사용하여 비동기 방식으로 코드 실행

다음 예제에서는 QueueUserWorkItem 메서드를 사용하여 ThreadProc 메서드로 나타낸 매우 간단한 작업을 대기시킵니다.

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

QueueUserWorkItem에 작업 데이터 제공

다음 코드 예제에서는 QueueUserWorkItem 메서드를 사용하여 작업을 대기시키고 작업 데이터를 제공합니다.

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

RegisterWaitForSingleObject 사용

다음 예제에서는 몇 가지 스레딩 기능을 보여 줍니다.

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

참고 항목

작업

방법: 작업에서 값 반환

참조

ThreadPool

Task

Task<TResult>

개념

작업 병렬 라이브러리

작업 병렬 라이브러리

스레드 및 스레딩

비동기 파일 I/O

타이머

기타 리소스

스레딩 개체 및 기능

변경 기록

날짜

변경 내용

이유

2010년 9월

새 스레드 생성에 대한 오래된 기본 크기 및 오래된 정보가 수정되었습니다. 작업 병렬 라이브러리의 예제가 추가되었습니다.

콘텐츠 버그 수정