다음을 통해 공유


작업 스케줄러

작업 스케줄러는 System.Threading.Tasks.TaskScheduler 클래스로 표현됩니다. 작업 스케줄러는 작업 항목이 실제로 실행되도록 합니다. 기본 작업 스케줄러는 부하 분산을 위한 작업 가로채기, 최대 처리량 및 전반적인 성능 향상을 위한 스레드 삽입/만료를 제공하는 .NET Framework 4 ThreadPool을 기반으로 합니다. 이 작업 스케줄러는 대부분의 시나리오에 적합합니다. 그러나, 특수한 기능이 필요한 경우에는 사용자 지정 스케줄러를 만들어 특정 작업 또는 쿼리에 대해 사용하도록 할 수 있습니다. 사용자 지정 작업 스케줄러를 만들고 사용하는 방법에 대한 자세한 내용은 방법: 동시성 수준을 제한하는 작업 스케줄러 만들기를 참조하십시오. 사용자 지정 스케줄러에 대한 추가적인 예제는 MSDN Code Gallery 웹 사이트에서 Parallel Extensions Samples를 참조하십시오.

기본 작업 스케줄러 및 ThreadPool

작업 병렬 라이브러리 및 PLINQ의 기본 스케줄러에서는 .NET Framework ThreadPool을 사용하여 작업을 큐에 넣고 실행합니다. .NET Framework 4에서 ThreadPoolSystem.Threading.Tasks.Task 형식이 제공하는 정보를 사용하여 병렬 작업 및 쿼리에서 흔히 나타나는 세부적인 병렬 처리(짧은 수명의 작업 단위)를 효율적으로 지원합니다.

ThreadPool 전역 큐와로컬 큐 비교

.NET Framework의 이전 버전에서와 마찬가지로 ThreadPool은 각 응용 프로그램 도메인의 스레드에 대해 전역 FIFO(선입선출) 작업 큐를 유지합니다. 프로그램에서 QueueUserWorkItem(또는 UnsafeQueueUserWorkItem)을 호출할 때마다 작업은 이 공유 큐에 넣어지고 다음 스레드가 사용 가능해지면 큐에서 제거되어 해당 스레드에 배치됩니다. .NET Framework 4에서 이 큐는 ConcurrentQueue 클래스와 유사한 잠금 없는 알고리즘을 사용하도록 개선되었습니다. ThreadPool에서는 이 잠금 없는 구현을 사용하여 작업 항목을 큐에 넣고 제거하는 데 걸리는 시간을 줄입니다. 이러한 성능상의 이점은 ThreadPool을 사용하는 모든 프로그램에 제공됩니다.

최상위 수준 작업은 다른 작업의 컨텍스트에서 만들어지지 않은 작업이며, 다른 작업 항목과 마찬가지로 전역 큐에 넣어집니다. 그러나 다른 작업의 컨텍스트에서 만들어진 중첩 작업 또는 자식 작업은 이와 상당히 다르게 처리됩니다. 자식 또는 중첩 작업은 부모 작업이 실행 중인 스레드에 고유한 로컬 큐에 넣어집니다. 부모 작업은 최상위 수준 작업이거나 다른 작업의 자식 작업일 수 있습니다. 이 스레드가 다른 작업을 더 처리할 준비가 되면 먼저 로컬 큐에서 작업을 찾습니다. 이 큐에 작업 항목이 있으면 신속하게 작업 항목에 액세스할 수 있습니다. 캐시 집약성을 유지하고 경합을 줄이기 위해 로컬 큐는 LIFO(후입선출) 방식으로 액세스됩니다. 자식 작업 및 중첩 작업에 대한 자세한 내용은 중첩된 작업 및 자식 작업을 참조하십시오.

다음 예제에서는 전역 큐에서 스케줄링되는 작업 및 로컬 큐에서 스케줄링되는 다른 작업을 보여 줍니다.

Sub QueueTasks()

    ' TaskA is a top level task.
    Dim taskA = Task.Factory.StartNew(Sub()

                                          Console.WriteLine("I was enqueued on the thread pool's global queue.")

                                          ' TaskB is a nested task and TaskC is a child task. Both go to local queue.
                                          Dim taskB = New Task(Sub() Console.WriteLine("I was enqueued on the local queue."))
                                          Dim taskC = New Task(Sub() Console.WriteLine("I was enqueued on the local queue, too."),
                                                                  TaskCreationOptions.AttachedToParent)

                                          taskB.Start()
                                          taskC.Start()

                                      End Sub)
End Sub
void QueueTasks()
{
    // TaskA is a top level task.
    Task taskA = Task.Factory.StartNew( () =>
    {                
        Console.WriteLine("I was enqueued on the thread pool's global queue."); 

        // TaskB is a nested task and TaskC is a child task. Both go to local queue.
        Task taskB = new Task( ()=> Console.WriteLine("I was enqueued on the local queue."));
        Task taskC = new Task(() => Console.WriteLine("I was enqueued on the local queue, too."),
                                TaskCreationOptions.AttachedToParent);

        taskB.Start();
        taskC.Start();

    });
}

로컬 큐를 사용하면 전역 큐에 대한 압력이 감소할 뿐 아니라 데이터 집약성을 활용할 수 있습니다. 로컬 큐의 작업 항목은 실제 메모리에서 다른 데이터 구조 가까이에 있는 데이터 구조를 자주 참조합니다. 이러한 경우 데이터는 첫 번째 작업 실행 이후 캐시에 이미 있기 때문에 신속히 액세스할 수 있습니다. PLINQ(병렬 LINQ)Parallel 클래스에서는 중첩 작업 및 자식 작업을 광범위하게 사용하며, 로컬 작업 큐를 사용하여 속도를 크게 향상시킵니다.

작업 가로채기

.NET Framework 4 ThreadPool에서는 다른 스레드의 큐에 작업이 남아 있는 경우 유휴 상태의 스레드가 없도록 하기 위해 작업 가로채기 알고리즘을 제공합니다. 스레드 풀의 스레드가 다른 작업을 처리할 준비가 되면 해당 스레드는 로컬 큐의 헤드, 전역 큐를 먼저 살펴본 후 다른 스레드의 로컬 큐를 확인합니다. 다른 스레드의 로컬 큐에 작업 항목이 있으면 먼저 경험적 접근 방법을 적용하여 해당 작업을 효율적으로 실행할 수 있는지 확인합니다. 실행 가능하면 작업 항목을 큐의 테일(FIFO 순서)에서 꺼내 가져옵니다. 이러한 과정을 통해 각 로컬 큐에서의 경합이 줄어들고 데이터 집약성이 유지됩니다. 이 아키텍처는 .NET Framework 4 ThreadPool에서 이전 버전과 달리 작업 부하를 더 효율적으로 분산하는 데 도움이 됩니다.

장기 실행 작업

작업이 로컬 큐에 배치되지 않도록 명시적으로 방지하려는 경우가 있을 수 있습니다. 예를 들어, 특정 작업 항목이 비교적 장기간 실행되고 로컬 큐의 다른 모든 작업 항목을 차단할 가능성이 있는 경우를 가정해 봅니다. 이 경우 LongRunning 옵션을 지정할 수 있습니다. 이 옵션은 특정 작업이 다른 스레드 또는 로컬 큐에 있는 작업 항목의 향후 진행을 차단하지 않도록 하기 위해 해당 작업을 처리할 추가 스레드가 필요할 수도 있음을 알려 주는 힌트를 스케줄러에 제공합니다. 이 옵션을 사용하면 전역 및 로컬 큐를 비롯하여 ThreadPool이 완전히 방지됩니다.

작업 인라이닝

대기 중인 작업은 대기 연산을 수행 중인 스레드에서 비동기적으로 실행될 수도 있습니다. 이렇게 하면 기존 스레드를 차단하지 않고 활용하여 스레드를 추가할 필요가 없어지므로 성능이 향상됩니다. 재진입으로 인한 오류를 방지하기 위해 작업 인라이닝은 관련된 스레드의 로컬 큐에서 대기 중인 대상이 있는 경우에만 발생합니다.

동기화 컨텍스트 지정

TaskScheduler.FromCurrentSynchronizationContext 메서드를 사용하면 작업이 특정 스레드에서의 실행을 위해 예약되도록 지정할 수 있습니다. 이 방법은 사용자 인터페이스 개체에 대한 액세스가 종종 해당 UI 개체를 만든 스레드에서 실행 중인 코드로 제한되는 Windows Forms 및 Windows Presentation Foundation 같은 프레임워크에서 유용합니다. 자세한 내용은 방법: 지정된 동기화 컨텍스트에 대한 작업 예약을 참조하십시오.

참고 항목

참조

TaskScheduler

개념

작업 병렬 라이브러리

방법: 지정된 동기화 컨텍스트에 대한 작업 예약

기타 리소스

방법: 동시성 수준을 제한하는 작업 스케줄러 만들기

작업 팩터리