작업 병렬 처리(작업 병렬 라이브러리)

업데이트: 2011년 3월

TPL(작업 병렬 라이브러리)은 이름이 암시하듯이 작업이란 개념을 기반으로 합니다. 작업 병렬 처리는 동시에 실행되는 하나 이상의 독립적인 작업을 의미합니다. 여기서 작업이란 비동기 작업을 의미하며 어떤 면에서는 새 스레드 또는 ThreadPool 작업 항목을 만드는 것과 비슷하지만 추상화 수준은 그보다 높습니다. 작업을 사용할 때의 주된 이점 두 가지는 다음과 같습니다.

  • 시스템 리소스를 더 효율적이고 확장 가능한 방식으로 사용할 수 있습니다.

    내부적으로 작업은 ThreadPool의 큐에 대기됩니다. ThreadPool은 처리량을 최대화하는 스레드 수를 확인하고 이에 맞게 스레드 수를 조정하는 언덕 오르기와 같은 알고리즘으로 향상되었습니다. 이로써 작업이 비교적 단순해지며, 여러 개의 작업을 만들어 세부적인 병렬 처리를 사용할 수 있습니다. 또한 이를 보완하기 위해 널리 알려진 작업 가로채기 알고리즘이 사용되어 부하 분산 기능을 제공합니다.

  • 프로그래밍 방식 제어 수준이 스레드 또는 작업 항목을 사용할 때보다 높아집니다.

    작업과 작업을 기반으로 만들어진 프레임워크는 대기, 취소, 연속, 강력한 예외 처리, 세부 상태, 사용자 지정 예약 등을 지원하는 강력한 API 집합을 제공합니다.

이 두 가지 이점 때문에 .NET Framework 4에서는 다중 스레드, 비동기 및 병렬 코드를 작성하기 위한 기본 API로 작업이 사용됩니다.

암시적으로 작업 만들기 및 실행

Parallel.Invoke 메서드를 사용하면 개수에 관계없이 여러 개의 임의 문을 간편하게 동시에 실행할 수 있습니다. 작업의 각 항목에 대한 Action 대리자를 전달하기만 하면 됩니다. 이러한 대리자를 만드는 가장 쉬운 방법은 람다 식을 사용하는 것입니다. 람다 식은 명명된 메서드를 호출하거나 코드를 인라인으로 제공합니다. 다음 예제에서는 동시에 실행되는 두 개의 작업을 만들고 시작하는 기본적인 Invoke 호출을 보여 줍니다.call that creates and starts two tasks that run concurrently.

참고참고

이 문서에서는 람다 식을 사용하여 TPL에 대리자를 정의합니다.C# 또는 Visual Basic의 람다 식에 익숙하지 않으면 PLINQ 및 TPL의 람다 식을 참조하십시오.

Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())
Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
참고참고

Invoke에 의해 자동으로 만들어지는 Task 인스턴스의 수는 제공되는 대리자의 수와 같지 않을 수도 있습니다.TPL에서는 대리자 수가 많은 경우 등에 다양한 최적화 기능을 사용할 수 있기 때문입니다.

자세한 내용은 방법: Parallel.Invoke를 사용하여 병렬 작업 실행을 참조하십시오.

작업 실행을 더 구체적으로 제어하거나 작업을 통해 값을 반환하려면 Task 개체를 더 명시적으로 사용해야 합니다.

명시적으로 작업 만들기 및 실행

작업은 System.Threading.Tasks.Task 클래스로 표현됩니다. 값을 반환하는 작업은 Task에서 상속하는 System.Threading.Tasks.Task<TResult> 클래스로 표현됩니다. 작업 개체는 인프라 세부 사항을 처리하고, 작업의 수명 기간 내내 호출 스레드에서 액세스할 수 있는 메서드 및 속성을 제공합니다. 예를 들어 언제든지 작업의 Status 속성에 액세스하여 작업이 실행되기 시작했는지, 이미 실행되어 완료되었는지, 취소되었는지, 또는 예외를 throw했는지를 확인할 수 있습니다. 이러한 상태는 TaskStatus 열거형으로 표현됩니다.

작업을 만들 때는 해당 작업에서 실행할 코드를 캡슐화하는 사용자 대리자를 지정합니다. 대리자는 명명된 대리자, 익명 메서드 또는 람다 식으로 표현할 수 있습니다. 람다 식에는 다음 예제에서처럼 명명된 메서드에 대한 호출을 포함할 수 있습니다.

        ' Create a task and supply a user delegate by using a lambda expression.
        Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))

        ' Start the task.
        taskA.Start()

        ' Output a message from the joining thread.
        Console.WriteLine("Hello from the joining thread.")

        ' Output:
        ' Hello from the joining thread.
        ' Hello from taskA. 

            // Create a task and supply a user delegate by using a lambda expression.
            var taskA = new Task(() => Console.WriteLine("Hello from taskA."));

            // Start the task.
            taskA.Start();

            // Output a message from the joining thread.
            Console.WriteLine("Hello from the calling thread.");


            /* Output:
             * Hello from the joining thread.
             * Hello from taskA. 
             */

StartNew 메서드를 사용하여 한 번에 작업을 만들고 시작할 수도 있습니다. 작업 만들기와 예약을 분리할 필요가 없는 경우에는 다음 예제와 같이 이 방법으로 작업을 만들고 시작하는 것이 좋습니다.

' Better: Create and start the task in one operation.
Dim taskA = Task.Factory.StartNew(Sub() Console.WriteLine("Hello from taskA."))

' Output a message from the joining thread.
Console.WriteLine("Hello from the joining thread.")
// Create and start the task in one operation.
var taskA = Task.Factory.StartNew(() => Console.WriteLine("Hello from taskA."));

// Output a message from the joining thread.
Console.WriteLine("Hello from the joining thread.");

작업은 TaskFactory의 기본 인스턴스를 반환하는 정적 Factory 속성을 노출하므로 이 메서드를 Task.Factory.StartNew(…)로 호출할 수 있습니다. 또한 이 예제에서 작업은 System.Threading.Tasks.Task<TResult> 형식이므로 계산 결과가 들어 있는 공용 Result 속성이 각 작업에 포함됩니다. 작업은 비동기적으로 실행되며 완료 순서에는 제한이 없습니다. 계산이 완료되기 전에 Result에 액세스할 경우 값을 사용할 수 있을 때까지 이 속성이 차단됩니다.

Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation1()),
                   Task(Of Double).Factory.StartNew(Function() DoComputation2()),
                   Task(Of Double).Factory.StartNew(Function() DoComputation3())}


Dim results() As Double
ReDim results(taskArray.Length)
For i As Integer = 0 To taskArray.Length
    results(i) = taskArray(i).Result
Next
Task<double>[] taskArray = new Task<double>[]
   {
       Task<double>.Factory.StartNew(() => DoComputation1()),

       // May be written more conveniently like this:
       Task.Factory.StartNew(() => DoComputation2()),
       Task.Factory.StartNew(() => DoComputation3())                
   };

double[] results = new double[taskArray.Length];
for (int i = 0; i < taskArray.Length; i++)
    results[i] = taskArray[i].Result;

자세한 내용은 방법: 작업에서 값 반환을 참조하십시오.

람다 식을 사용하여 작업 대리자를 만드는 경우 소스 코드의 해당 지점에서 표시되는 모든 변수에 액세스할 수 있습니다. 그러나 특히 루프 내에서 람다가 예상되는 변수를 capture하지 않는 경우가 있습니다. 이 경우 람다는 각 반복 후에 변경할 때 값이 아닌 최종 값만 capture합니다. 다음 예제와 같이 생성자를 통해 작업에 상태 개체를 제공하여 각 반복의 값에 액세스할 수 있습니다.


    Class MyCustomData

        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Sub TaskDemo2()
        ' Create the task object by using an Action(Of Object) to pass in custom data
        ' in the Task constructor. This is useful when you need to capture outer variables
        ' from within a loop. 
        ' As an experiement, try modifying this code to capture i directly in the lamda,
        ' and compare results.
        Dim taskArray() As Task
        ReDim taskArray(10)
        For i As Integer = 0 To taskArray.Length - 1
            taskArray(i) = New Task(Sub(obj As Object)
                                        Dim mydata = CType(obj, MyCustomData)
                                        mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                        Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.",
                                                          mydata.Name, mydata.CreationTime, mydata.ThreadNum)
                                    End Sub,
            New MyCustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks}
            )
            taskArray(i).Start()
        Next

    End Sub


       class MyCustomData
       {
        public long CreationTime;
        public int Name;
        public int ThreadNum;
        }

    void TaskDemo2()
    {
        // Create the task object by using an Action(Of Object) to pass in custom data
        // in the Task constructor. This is useful when you need to capture outer variables
        // from within a loop. As an experiement, try modifying this code to 
        // capture i directly in the lambda, and compare results.
        Task[] taskArray = new Task[10];

        for(int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = new Task((obj) =>
                {
                                        MyCustomData mydata = (MyCustomData) obj;
                                        mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                        Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.",
                                                          mydata.Name, mydata.CreationTime, mydata.ThreadNum)
                },
            new MyCustomData () {Name = i, CreationTime = DateTime.Now.Ticks}
            );
            taskArray[i].Start();
        }
    }

이 상태는 작업 대리자에 인수로 전달되며 작업 개체에서 AsyncState 속성을 사용하여 액세스할 수 있습니다. 또한 일부 시나리오에서는 생성자를 통해 데이터를 전달하여 작은 성능 이점을 얻을 수도 있습니다.

작업 ID

모든 작업은 응용 프로그램 도메인에서 작업을 고유하게 식별하는 정수 ID를 받으며, 이 ID는 Id 속성을 사용하여 액세스할 수 있습니다. ID는 Visual Studio 디버거의 병렬 스택병렬 작업 창에서 작업 정보를 보는 데 유용합니다. ID는 나중에 만들어집니다. 즉, 요청될 때까지는 ID가 만들어지지 않으므로 프로그램이 실행될 때마다 작업 ID가 달라질 수 있습니다. 디버거에서 작업 ID를 보는 방법에 대한 자세한 내용은 병렬 스택 창 사용을 참조하십시오.

작업 생성 옵션

작업을 만드는 대부분의 API는 TaskCreationOptions 매개 변수를 사용하는 오버로드를 제공합니다. 이러한 옵션 중 하나를 지정하면 스레드 풀에서 작업을 예약하는 방법을 작업 스케줄러에 지시할 수 있습니다. 다음 표에서는 다양한 작업 생성 옵션을 보여 줍니다.

요소

설명

None

옵션을 지정하지 않을 경우 기본 옵션으로 사용됩니다. 스케줄러에서는 스케줄러의 기본 휴리스틱을 사용하여 작업을 예약합니다.

PreferFairness

먼저 만들어진 작업은 먼저 실행될 가능성이 높고 나중에 만들어진 작업은 나중에 실행될 가능성이 높게 작업이 예약되도록 지정합니다.

LongRunning

작업이 장기 실행 작업을 나타냄을 지정합니다.

AttachedToParent

작업을 현재 작업(있는 경우)의 연결된 자식 작업으로 만들어야 함을 지정합니다. 자세한 내용은 중첩된 작업 및 자식 작업을 참조하십시오.

이러한 옵션과 비트 OR 연산을 함께 사용할 수 있습니다. 다음 예제에서는 LongRunningPreferFairness 옵션이 있는 작업을 보여 줍니다.


Dim task3 = New Task(Sub() MyLongRunningMethod(),
                        TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()
var task3 = new Task(() => MyLongRunningMethod(),
                    TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();

작업 연속 만들기

Task.ContinueWithTask<TResult>.ContinueWith 메서드를 사용하여 선행 작업이 완료된 후에 작업이 시작되도록 지정할 수 있습니다. 이때 연속 작업의 대리자가 선행 작업의 상태를 확인할 수 있도록 해당 대리자에 선행 작업에 대한 참조를 전달합니다. 또한 선행 작업의 출력이 연속 작업의 입력으로 사용될 수 있도록 Result 속성에서 선행 작업의 사용자 정의 값을 연속 작업에 전달할 수 있습니다. 다음 예제에서는 프로그램 코드에 의해 getData가 시작되고, getData가 완료될 때 analyzeData가 시작되며, analyzeData 가 완료되면 reportData가 시작됩니다. getData는 결과로 바이트 배열을 생성하며 이 배열은 analyzeData에 전달됩니다. analyzeData는 이 배열을 처리하고 Analyze 메서드의 반환 형식에서 유추된 형식을 갖는 결과를 반환합니다. reportData는 analyzeData에서 입력을 받고, 비슷한 방식으로 유추된 형식을 가지며 프로그램에서 사용할 수 있는 결과를 Result 속성에 생성합니다.

        Dim getData As Task(Of Byte()) = New Task(Of Byte())(Function() GetFileData())
        Dim analyzeData As Task(Of Double()) = getData.ContinueWith(Function(x) Analyze(x.Result))
        Dim reportData As Task(Of String) = analyzeData.ContinueWith(Function(y As Task(Of Double)) Summarize(y.Result))

        getData.Start()

        System.IO.File.WriteAllText("C:\reportFolder\report.txt", reportData.Result)

            Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
            Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
            Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));

            getData.Start();

            //or...
            Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
                                        .ContinueWith((x) => Analyze(x.Result))
                                        .ContinueWith((y) => Summarize(y.Result));

            System.IO.File.WriteAllText(@"C:\reportFolder\report.txt", reportData.Result);



ContinueWhenAllContinueWhenAny 메서드를 사용하면 여러 작업을 연속적으로 수행할 수 있습니다. 자세한 내용은 연속 작업방법: 연속 작업을 사용하여 여러 작업 연결을 참조하십시오.

분리된 상태의 중첩된 작업 만들기

작업에서 실행되는 사용자 코드를 통해 새 작업이 만들어지지만 AttachedToParent 옵션은 지정되지 않을 경우 새 작업은 어떤 특수한 방법으로도 외부 작업과 동기화되지 않습니다. 이러한 작업을 분리된 상태의 중첩된 작업이라고 합니다. 다음 예제에서는 분리된 상태의 중첩된 작업을 하나 만드는 작업을 보여 줍니다.

Dim outer = Task.Factory.StartNew(Sub()
                                      Console.WriteLine("Outer task beginning.")
                                      Dim child = Task.Factory.StartNew(Sub()
                                                                            Thread.SpinWait(5000000)
                                                                            Console.WriteLine("Detached task completed.")
                                                                        End Sub)
                                  End Sub)
outer.Wait()
Console.WriteLine("Outer task completed.")

' Output:
'     Outer task beginning.
'     Outer task completed.
'    Detached child completed.
            var outer = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Outer task beginning.");

                var child = Task.Factory.StartNew(() =>
                {
                    Thread.SpinWait(5000000);
                    Console.WriteLine("Detached task completed.");
                });

            });

            outer.Wait();
            Console.WriteLine("Outer task completed.");

            /* Output:
                Outer task beginning.
                Outer task completed.
                Detached task completed.

             */

외부 작업은 중첩된 작업이 완료될 때까지 대기하지 않습니다.

자식 작업 만들기

작업에서 실행되는 사용자 코드를 통해 AttachedToParent 옵션이 지정된 작업이 만들어질 때 이 새 작업은 원래 작업, 즉 부모 작업의 자식 작업이라고 합니다. AttachedToParent 옵션을 사용하면 부모 작업은 암시적으로 모든 자식 작업이 완료될 때까지 대기하게 되므로 이 옵션을 사용하여 구조적 작업 병렬 처리를 표현할 수 있습니다. 다음 예제에서는 하나의 자식 작업을 만드는 작업을 보여 줍니다.

Dim parent = Task.Factory.StartNew(Sub()
                                       Console.WriteLine("Parent task beginning.")
                                       Dim child = Task.Factory.StartNew(Sub()
                                                                             Thread.SpinWait(5000000)
                                                                             Console.WriteLine("Attached child completed.")
                                                                         End Sub,
                                                                         TaskCreationOptions.AttachedToParent)

                                   End Sub)
outer.Wait()
Console.WriteLine("Parent task completed.")

' Output:
'     Parent task beginning.
'     Attached child completed.
'     Parent task completed.
var parent = Task.Factory.StartNew(() =>
{
    Console.WriteLine("Parent task beginning.");

    var child = Task.Factory.StartNew(() =>
    {
        Thread.SpinWait(5000000);
        Console.WriteLine("Attached child completed.");
    }, TaskCreationOptions.AttachedToParent);

});

parent.Wait();
Console.WriteLine("Parent task completed.");

/* Output:
    Parent task beginning.
    Attached task completed.
    Parent task completed.
 */

자세한 내용은 중첩된 작업 및 자식 작업을 참조하십시오.

작업에서 대기

System.Threading.Tasks.Task 형식과 System.Threading.Tasks.Task<TResult> 형식에서는 작업이 완료될 때까지 대기할 수 있게 해 주는 Task.WaitTask<TResult>.Wait 메서드의 몇 가지 오버로드를 제공합니다. 또한 정적 Task.WaitAllTask.WaitAny 메서드의 오버로드를 사용하면 작업 배열의 작업이 하나라도 완료되거나 모두 완료될 때까지 대기할 수 있습니다.

일반적으로 다음과 같은 경우에 작업을 대기시킵니다.

  • 주 스레드에서 작업에 의해 계산된 최종 결과를 사용해야 하는 경우

  • 작업에서 throw될 수 있는 예외를 처리해야 하는 경우

다음 예제에서는 예외 처리가 포함되지 않은 기본적인 패턴을 보여 줍니다.

Dim tasks() =
{
    Task.Factory.StartNew(Sub() MethodA()),
    Task.Factory.StartNew(Sub() MethodB()),
    Task.Factory.StartNew(Sub() MethodC())
}

' Block until all tasks complete.
Task.WaitAll(tasks)

' Continue on this thread...
Task[] tasks = new Task[3]
{
    Task.Factory.StartNew(() => MethodA()),
    Task.Factory.StartNew(() => MethodB()),
    Task.Factory.StartNew(() => MethodC())
};

//Block until all tasks complete.
Task.WaitAll(tasks);

// Continue on this thread...

예외 처리를 보여 주는 예제는 방법: 작업에서 throw된 예외 처리를 참조하십시오.

일부 오버로드에서는 제한 시간을 지정할 수 있으며, 다른 오버로드에서는 프로그래밍 방식으로 또는 사용자 입력에 대한 응답으로 대기 자체를 취소할 수 있도록 추가 CancellationToken을 입력 매개 변수로 사용합니다.

작업에서 대기할 때 이는 TaskCreationOptions AttachedToParent 옵션을 사용하여 만들어진 해당 작업의 자식 작업이 모두 완료될 때까지 대기해야 함을 의미합니다. Task.Wait는 작업이 이미 완료되었는지 여부를 즉시 반환합니다. 작업에서 발생한 예외는 모두 Wait 메서드에 의해 throw됩니다. 이는 Wait 메서드가 작업 완료 후 호출된 경우에도 해당됩니다.

자세한 내용은 방법: 하나 이상의 작업을 완료할 때까지 대기를 참조하십시오.

작업의 예외 처리

작업에서 하나 이상의 예외를 throw하면 해당 예외가 AggregateException에 래핑됩니다. 이 예외는 해당 작업과 조인하는 스레드에 다시 전파됩니다. 이 스레드는 일반적으로 해당 작업에서 대기하거나 해당 작업의 Result 속성에 액세스하려고 시도하는 스레드입니다. 이 동작을 통해 처리되지 않은 예외가 발생할 때마다 기본적으로 프로세스를 중지하도록 하는 .NET Framework 정책을 적용할 수 있습니다. 호출하는 코드에서는 작업 또는 작업 그룹에 Wait, WaitAll 또는 WaitAny 메서드나 Result() 속성을 사용하고 Wait 메서드를 try-catch 블록에 넣어 예외를 처리할 수 있습니다.

또한 조인하는 스레드에서는 작업이 가비지 수집되기 전에 Exception 속성에 액세스하여 예외를 처리할 수 있습니다. 이 속성에 액세스하면 처리되지 않은 예외로 인해 개체가 종료될 때 프로세스를 중지하는 예외 전파 동작이 트리거되는 것을 방지할 수 있습니다.

예외 및 작업에 대한 자세한 내용은 예외 처리(작업 병렬 라이브러리)방법: 작업에서 throw된 예외 처리를 참조하십시오.

작업 취소

Task 클래스는 협조적 취소를 지원하며, .NET Framework 버전 4에 새로 추가된 System.Threading.CancellationTokenSourceSystem.Threading.CancellationToken 클래스와 완전히 통합됩니다. System.Threading.Tasks.Task 클래스의 많은 생성자는 CancellationToken을 입력 매개 변수로 사용합니다. StartNew 오버로드의 상당수도 CancellationToken을 사용합니다.

이 토큰을 만든 다음 나중에 CancellationTokenSource 클래스를 사용하여 취소 요청을 실행할 수 있습니다. 이 토큰을 Task에 인수로 전달하고, 취소 요청에 대한 응답 작업을 수행하는 사용자 대리자에서도 동일한 토큰을 참조합니다. 자세한 내용은 작업 취소방법: 작업 및 해당 자식 취소를 참조하십시오.

TaskFactory 클래스

TaskFactory 클래스에서는 작업 및 연속 작업을 만들고 시작하기 위한 몇 가지 일반적인 패턴을 캡슐화하는 정적 메서드를 제공합니다.

  • 가장 일반적인 패턴은 하나의 문으로 작업을 만들고 시작하는 StartNew입니다. 자세한 내용은 StartNew()를 참조하십시오.

  • 여러 선행 작업에서 연속 작업을 만들 때는 ContinueWhenAll 메서드 또는 ContinueWhenAny 메서드나 Task<TResult> 클래스에서 이에 해당하는 메서드를 사용합니다. 자세한 내용은 연속 작업을 참조하십시오.

  • 비동기 프로그래밍 모델인 BeginX 및 EndX 메서드를 Task 또는 Task<TResult> 인스턴스에 캡슐화하려면 FromAsync 메서드를 사용합니다. 자세한 내용은 TPL 및 일반적인 .NET 비동기 프로그래밍을 참조하십시오.

기본 TaskFactoryTask 클래스나 Task<TResult> 클래스에서 정적 속성으로 액세스할 수 있습니다. TaskFactory를 직접 인스턴스화하고 CancellationToken, TaskCreationOptions 옵션, TaskContinuationOptions 옵션 또는 TaskScheduler를 포함한 다양한 옵션을 지정할 수도 있습니다. 작업 팩터리를 만들 때 어떤 옵션을 지정하든 작업 팩터리에서 만드는 모든 작업에 해당 옵션이 적용됩니다. 단, TaskCreationOptions 열거형을 사용하여 작업을 만드는 경우는 제외되며, 이때는 해당 작업의 옵션이 작업 팩터리의 옵션보다 우선합니다.

대리자가 없는 작업

일부 경우에는 Task를 사용하여 사용자 대리자 대신 외부 구성 요소에서 수행되는 비동기 작업을 캡슐화할 수 있습니다. 이 작업이 비동기 프로그래밍 모델인 Begin/End 패턴을 기반으로 하는 경우에는 FromAsync 메서드를 사용할 수 있습니다. 그렇지 않은 경우에는 TaskCompletionSource<TResult> 개체를 사용하여 작업에서 수행할 비동기 작업을 래핑하고 이를 통해 예외 전파 및 연속에 대한 지원과 같은 Task 프로그래밍 기능의 몇 가지 이점을 얻을 수 있습니다. 자세한 내용은 TaskCompletionSource<TResult>을 참조하십시오.

사용자 지정 스케줄러

대부분의 응용 프로그램 또는 라이브러리 개발자는 작업이 실행되는 프로세서, 작업과 다른 작업이 동기화되는 방식 또는 System.Threading.ThreadPool에서 작업이 예약되는 방식에는 크게 신경 쓰지 않고, 단지 작업이 호스트 컴퓨터에서 가능한 한 효율적으로 실행되기만을 바랍니다. 예약 세부 사항을 보다 세부적으로 제어해야 하는 경우 작업 병렬 라이브러리를 사용하면 기본 작업 스케줄러의 일부 설정을 구성할 수 있으며 사용자 지정 스케줄러를 지정할 수도 있습니다. 자세한 내용은 TaskScheduler를 참조하십시오.

관련 데이터 구조

TPL에는 병렬 시나리오와 순차 시나리오 모두에 유용한 새로운 공용 형식이 몇 가지 포함되어 있습니다. 여기에는 스레드로부터 안전하며 속도 및 확장성이 우수한 System.Collections.Concurrent 네임스페이스의 몇 가지 컬렉션 클래스뿐 아니라 특정 종류의 작업 부하에 대해 이전보다 높은 효율성을 제공하는 SemaphoreLock 및 System.Threading.ManualResetEventSlim 등의 새로운 몇 가지 동기화 형식도 포함됩니다. .NET Framework 버전 4에 새로 추가된 System.Threading.BarrierSystem.Threading.SpinLock 등의 다른 형식은 이전 버전에서는 사용할 수 없었던 기능을 제공합니다. 자세한 내용은 병렬 프로그래밍을 위한 데이터 구조를 참조하십시오.

사용자 지정 작업 형식

System.Threading.Tasks.Task 또는 System.Threading.Tasks.Task<TResult>에서 상속하지 않는 것이 좋습니다. 대신 AsyncState 속성을 사용하여 추가 데이터 또는 상태를 Task 또는 Task<TResult> 개체에 연결합니다. 확장 메서드를 사용하여 TaskTask<TResult> 클래스의 기능을 확장할 수도 있습니다. 확장 메서드에 대한 자세한 내용은 확장 메서드(C# 프로그래밍 가이드)확장 메서드(Visual Basic)를 참조하십시오.

Task 또는 Task<TResult>에서 상속해야 하는 경우에는 System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult> 또는 System.Threading.Tasks.TaskCompletionSource<TResult> 클래스를 사용하여 사용자 지정 작업 형식의 인스턴스를 만들 수 없습니다. 이 클래스는 TaskTask<TResult> 개체만 만들기 때문입니다. 또한 Task, Task<TResult>, TaskFactoryTaskFactory<TResult>에서 제공하는 작업 연속 메커니즘을 사용하여 사용자 지정 작업의 인스턴스를 만들 수 없습니다. 이 메커니즘에서도 mechanisms also create only TaskTask<TResult> 개체만 만들기 때문입니다.

관련 항목

제목

설명

연속 작업

연속 작업이 실행되는 방식을 설명합니다.

중첩된 작업 및 자식 작업

자식 작업과 중첩된 작업의 차이점을 설명합니다.

작업 취소

Task 클래스로 빌드되는 취소 지원에 대해 설명합니다.

예외 처리(작업 병렬 라이브러리)

동시 스레드에 대한 예외를 처리하는 방법을 설명합니다.

방법: Parallel.Invoke를 사용하여 병렬 작업 실행

Invoke를 사용하는 방법을 설명합니다.

방법: 작업에서 값 반환

작업을 통해 값을 반환하는 방법을 설명합니다.

방법: 하나 이상의 작업을 완료할 때까지 대기

작업을 기다리는 방법을 설명합니다.

방법: 작업 및 해당 자식 취소

작업을 취소하는 방법을 설명합니다.

방법: 작업에서 throw된 예외 처리

작업을 통해 throw된 예외를 처리하는 방법을 설명합니다.

방법: 연속 작업을 사용하여 여러 작업 연결

다른 작업이 완료될 때 작업을 실행하는 방법을 설명합니다.

방법: 병렬 작업을 사용하여 이진 트리 트래버스

작업을 사용하여 이진 트리를 따라 이동하는 방법을 설명합니다.

데이터 병렬 처리(작업 병렬 라이브러리)

ForForEach를 사용하여 데이터에 대한 병렬 루프를 만드는 방법을 설명합니다.

.NET Framework의 병렬 프로그래밍

.NET 병렬 프로그래밍의 최상위 노드입니다.

참고 항목

개념

.NET Framework의 병렬 프로그래밍

변경 기록

날짜

변경 내용

이유

2011년 3월

TaskTask<TResult> 클래스에서 상속하는 방법에 대한 정보를 추가했습니다.

향상된 기능 관련 정보