다음을 통해 공유


관리되는 스레드 풀

업데이트: 2007년 11월

ThreadPool 클래스를 사용하면 시스템에서 관리하는 작업자 스레드 풀이 응용 프로그램에 제공되므로 사용자는 스레드 관리가 아닌 응용 프로그램 작업에 집중할 수 있습니다. 백그라운드 프로세스가 필요한 짧은 작업이 있는 경우 관리되는 스레드 풀을 사용하여 다중 스레드를 활용할 수 있습니다.

참고:

.NET Framework 버전 2.0 서비스 팩 1부터는 이전 릴리스의 .NET Framework에서 병목 현상이 발생하는 것으로 파악된 세 개의 주요 영역인 작업을 큐에 대기시키기, 스레드 풀 스레드 디스패치 및 I/O 완료 스레드 디스패치에서 스레드 풀의 처리량이 크게 향상되었습니다. 이 기능을 사용하려면 응용 프로그램이 .NET Framework 버전 3.5를 대상으로 해야 합니다. 자세한 내용은 .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에서 공용 언어 런타임은 스레드 풀 스레드에서 처리되지 않은 예외를 포착합니다. 이렇게 하면 응용 프로그램 상태가 손상되어 결국 응용 프로그램이 중단될 수 있고, 이에 따라 디버깅이 어려워질 수 있습니다.

최대 스레드 풀 스레드 수

스레드 풀의 큐에 들어갈 수 있는 작업 수는 사용할 수 있는 메모리에 의해서만 제한됩니다. 그러나 스레드 풀이 동시에 프로세스에서 활성화될 수 있는 스레드 수를 제한합니다. 기본적으로 제한되는 수는 CPU당 25 작업자 스레드 및 1,000 I/O 완료 스레드입니다.

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

참고:

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

최소 유휴 스레드 수

스레드 풀은 모든 스레드가 유휴 상태인 경우에도 사용 가능한 최소 스레드 수를 유지하므로 대기 중인 작업을 즉시 시작할 수 있습니다. 이 최소 수를 초과하는 유휴 스레드는 시스템 리소스 저장을 위해 종료됩니다. 기본적으로 프로세서당 하나의 유휴 스레드를 유지합니다.

스레드 풀에는 새 유휴 스레드가 시작되기 전에 기본으로 제공되는 지연 시간(.NET Framework 버전 2.0의 경우 0.5초)이 있습니다. 응용 프로그램에서 주기적으로 짧은 시간에 많은 작업을 시작하는 경우 유휴 스레드 수를 조금 늘려도 처리량이 크게 증가할 수 있습니다. 유휴 스레드 수를 너무 높게 설정하면 시스템 리소스가 불필요하게 사용됩니다.

GetMinThreadsSetMinThreads 메서드를 사용하여 스레드 풀에서 유지하는 유휴 스레드 수를 제어할 수 있습니다.

참고:

.NET Framework 버전 1.0에서는 최소 유휴 스레드 수를 설정할 수 없습니다.

보안 검사 건너뛰기

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

스레드 풀 사용

관리 코드에서 ThreadPool.QueueUserWorkItem을 호출하거나 비관리 코드에서 CorQueueUserWorkItem을 호출하고 작업을 수행하는 메서드를 나타내는 WaitCallback 대리자를 전달하여 스레드 풀을 사용합니다. ThreadPool.RegisterWaitForSingleObject 메서드를 사용하거나, 신호를 받거나 시간이 초과되었을 때 WaitOrTimerCallback 대리자에서 나타내는 메서드를 호출하는 WaitHandle을 전달하여 대기 작업에 관련된 작업 항목을 대기시킬 수도 있습니다. 두 가지 경우 모두 스레드 풀은 콜백 메서드를 호출하는 백그라운드 스레드를 사용합니다.

ThreadPool 예제

다음의 세 가지 코드 예제에서는 QueueUserWorkItemRegisterWaitForSingleObject 메서드를 보여 줍니다.

첫 번째 예제에서는 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.");
    }
}

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

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

참고 항목

개념

스레드 및 스레딩

비동기 파일 I/O

타이머

참조

ThreadPool

기타 리소스

스레딩 개체 및 기능