작업 기반 비동기 프로그래밍Task-based asynchronous programming

TPL(작업 병렬 라이브러리)은 작업이란 개념을 기반으로 하며 비동기 작업을 나타냅니다.The Task Parallel Library (TPL) is based on the concept of a task, which represents an asynchronous operation. 몇 가지 점에서 작업은 스레드 또는 ThreadPool 작업 항목과 비슷하지만 추상화 수준은 더 높습니다.In some ways, a task resembles a thread or ThreadPool work item, but at a higher level of abstraction. 작업 병렬 처리는 동시에 실행되는 하나 이상의 독립적인 작업을 의미합니다.The term task parallelism refers to one or more independent tasks running concurrently. 작업을 사용할 때의 주된 이점 두 가지는 다음과 같습니다.Tasks provide two primary benefits:

  • 시스템 리소스를 더 효율적이고 확장 가능한 방식으로 사용할 수 있습니다.More efficient and more scalable use of system resources.

    내부적으로 작업은 ThreadPool의 큐에 대기됩니다. 이 ThreadPool은 스레드 수를 파악하여 이에 맞게 조정하고, 처리량을 최대화하는 부하 분산을 제공하는 알고리즘을 사용하여 기능이 향상되었습니다.Behind the scenes, tasks are queued to the ThreadPool, which has been enhanced with algorithms that determine and adjust to the number of threads and that provide load balancing to maximize throughput. 이로써 작업이 비교적 단순해지며, 여러 개의 작업을 만들어 세부적인 병렬 처리를 사용할 수 있습니다.This makes tasks relatively lightweight, and you can create many of them to enable fine-grained parallelism.

  • 프로그래밍 방식 제어 수준이 스레드 또는 작업 항목을 사용할 때보다 높아집니다.More programmatic control than is possible with a thread or work item.

    작업과 작업을 기반으로 만들어진 프레임워크는 대기, 취소, 연속, 강력한 예외 처리, 세부 상태, 사용자 지정 예약 등을 지원하는 강력한 API 집합을 제공합니다.Tasks and the framework built around them provide a rich set of APIs that support waiting, cancellation, continuations, robust exception handling, detailed status, custom scheduling, and more.

이 두 가지 이유 때문에 .NET Framework에서는 다중 스레드, 비동기 및 병렬 코드를 작성하는 API로 TPL이 선호됩니다.For both of these reasons, in the .NET Framework, TPL is the preferred API for writing multi-threaded, asynchronous, and parallel code.

암시적으로 작업 만들기 및 실행Creating and running tasks implicitly

Parallel.Invoke 메서드를 사용하면 개수에 관계없이 여러 개의 임의 문을 간편하게 동시에 실행할 수 있습니다.The Parallel.Invoke method provides a convenient way to run any number of arbitrary statements concurrently. 작업의 각 항목에 대한 Action 대리자를 전달하기만 하면 됩니다.Just pass in an Action delegate for each item of work. 이러한 대리자를 만드는 가장 쉬운 방법은 람다 식을 사용하는 것입니다.The easiest way to create these delegates is to use lambda expressions. 람다 식은 명명된 메서드를 호출하거나 코드를 인라인으로 제공합니다.The lambda expression can either call a named method or provide the code inline. 다음 예제에서는 동시에 실행되는 두 개의 작업을 만들고 시작하는 기본적인 Invoke 호출을 보여 줍니다.call that creates and starts two tasks that run concurrently.The following example shows a basic Invoke call that creates and starts two tasks that run concurrently. 이름이 DoSomeWork인 메서드를 호출하는 람다 식에서 첫 번째 작업을 표시하며 이름이 DoSomeOtherWork인 메서드를 호출하는 람다 식에서 두 번째 작업을 표시합니다.The first task is represented by a lambda expression that calls a method named DoSomeWork, and the second task is represented by a lambda expression that calls a method named DoSomeOtherWork.

참고

이 문서에서는 람다 식을 사용하여 TPL에 대리자를 정의합니다.This documentation uses lambda expressions to define delegates in TPL. C# 또는 Visual Basic의 람다 식을 잘 모르는 경우 PLINQ 및 TPL의 람다 식을 참조하세요.If you are not familiar with lambda expressions in C# or Visual Basic, see Lambda Expressions in PLINQ and TPL.

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

참고

Task에 의해 자동으로 만들어지는 Invoke 인스턴스의 수는 제공되는 대리자의 수와 같지 않을 수도 있습니다.The number of Task instances that are created behind the scenes by Invoke is not necessarily equal to the number of delegates that are provided. TPL에서는 대리자 수가 많은 경우 등에 다양한 최적화 기능을 사용할 수 있기 때문입니다.The TPL may employ various optimizations, especially with large numbers of delegates.

자세한 내용은 방법: Parallel.Invoke를 사용하여 병렬 작업 실행을 참조하세요.For more information, see How to: Use Parallel.Invoke to Execute Parallel Operations.

작업 실행을 더 구체적으로 제어하거나 작업을 통해 값을 반환하려면 Task 개체를 더 명시적으로 사용해야 합니다.For greater control over task execution or to return a value from the task, you have to work with Task objects more explicitly.

명시적으로 작업 만들기 및 실행Creating and running tasks explicitly

System.Threading.Tasks.Task 클래스로 표현되는 값을 반환하지 않는 작업입니다.A task that does not return a value is represented by the System.Threading.Tasks.Task class. 값을 반환하는 작업은 System.Threading.Tasks.Task<TResult>에서 상속하는 Task 클래스로 표현됩니다.A task that returns a value is represented by the System.Threading.Tasks.Task<TResult> class, which inherits from Task. 작업 개체는 인프라 세부 사항을 처리하고, 작업의 수명 내내 호출 스레드에서 액세스할 수 있는 메서드 및 속성을 제공합니다.The task object handles the infrastructure details and provides methods and properties that are accessible from the calling thread throughout the lifetime of the task. 예를 들어 언제든지 작업의 Status 속성에 액세스하여 작업이 실행되기 시작했는지, 이미 실행되어 완료되었는지, 취소되었는지, 또는 예외를 throw했는지를 확인할 수 있습니다.For example, you can access the Status property of a task at any time to determine whether it has started running, ran to completion, was canceled, or has thrown an exception. 이러한 상태는 TaskStatus 열거형으로 표현됩니다.The status is represented by a TaskStatus enumeration.

작업을 만들 때는 해당 작업에서 실행할 코드를 캡슐화하는 사용자 대리자를 지정합니다.When you create a task, you give it a user delegate that encapsulates the code that the task will execute. 대리자는 명명된 대리자, 익명 메서드 또는 람다 식으로 표현할 수 있습니다.The delegate can be expressed as a named delegate, an anonymous method, or a lambda expression. 람다 식에는 다음 예제에서처럼 명명된 메서드에 대한 호출을 포함할 수 있습니다.Lambda expressions can contain a call to a named method, as shown in the following example. 예제에는 콘솔 모드 애플리케이션이 종료되기 전에 작업 실행이 완료되도록 Task.Wait 메서드에 대한 호출이 포함됩니다.Note that the example includes a call to the Task.Wait method to ensure that the task completes execution before the console mode application ends.

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

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

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

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.", 
                        Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Thread.CurrentThread.Name = "Main"
        
        ' 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 calling thread.
        Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name)
        taskA.Wait()
   End Sub
End Module
' The example displays output like the following:
'    Hello from thread 'Main'.
'    Hello from taskA.

Task.Run 메서드를 사용하여 한 번에 작업을 만들고 시작할 수도 있습니다.You can also use the Task.Run methods to create and start a task in one operation. 작업을 관리하기 위해 Run 메서드는 기본 작업 스케줄러를 사용합니다. 이 경우 작업 스케줄러가 현재 스레드와 연결되었는지 여부는 관계없습니다.To manage the task, the Run methods use the default task scheduler, regardless of which task scheduler is associated with the current thread. Run 메서드는 작업을 만들고 작업 일정을 예약할 때 더 자세히 제어할 필요가 없는 경우 작업을 만들고 시작하는 데 가장 많이 사용되는 방법입니다.The Run methods are the preferred way to create and start tasks when more control over the creation and scheduling of the task is not needed.

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

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Define and run the task.
      Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Thread.CurrentThread.Name = "Main"
        
        Dim taskA As Task = Task.Run(Sub() Console.WriteLine("Hello from taskA."))

        ' Output a message from the calling thread.
        Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name)
        taskA.Wait()
   End Sub
End Module
' The example displays output like the following:
'    Hello from thread 'Main'.
'    Hello from taskA.

TaskFactory.StartNew 메서드를 사용하여 한 번에 작업을 만들고 시작할 수도 있습니다.You can also use the TaskFactory.StartNew method to create and start a task in one operation. 생성 및 일정 예약을 구분할 필요가 없고 추가 작업 생성 옵션이나 특정 스케줄러를 사용할 필요가 있는 경우 또는 다음 예제와 같이 Task.AsyncState 속성을 통해 검색할 수 있는 작업에 추가 상태를 전달해야 하는 경우 이 메서드를 사용합니다.Use this method when creation and scheduling do not have to be separated and you require additional task creation options or the use of a specific scheduler, or when you need to pass additional state into the task that you can retrieve through its Task.AsyncState property, as shown in the following example.

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

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

public class Example
{
   public static void Main()
   {
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
      foreach (var task in taskArray) {
         var data = task.AsyncState as CustomData;
         if (data != null)
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum);
      }                     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

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

Module Example
   Public Sub Main()
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     

      For Each task In taskArray
         Dim data = TryCast(task.AsyncState, CustomData)
         If data IsNot Nothing Then
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum)
         End If
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task #0 created at 635116451245250515, ran on thread #3, RanToCompletion
'    Task #1 created at 635116451245270515, ran on thread #4, RanToCompletion
'    Task #2 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #3 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #4 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #5 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #6 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #7 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #8 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #9 created at 635116451245270515, ran on thread #3, RanToCompletion

TaskTask<TResult>는 각각 Factory의 기본 인스턴스를 반환하는 정적 TaskFactory 속성을 노출하므로 메서드를 Task.Factory.StartNew()로 호출할 수 있습니다.Task and Task<TResult> each expose a static Factory property that returns a default instance of TaskFactory, so that you can call the method as Task.Factory.StartNew(). 또한 다음 예제에서 작업은 System.Threading.Tasks.Task<TResult> 형식이므로 계산 결과가 들어 있는 공용 Task<TResult>.Result 속성이 각 작업에 포함됩니다.Also, in the following example, because the tasks are of type System.Threading.Tasks.Task<TResult>, they each have a public Task<TResult>.Result property that contains the result of the computation. 작업은 비동기적으로 실행되며 완료 순서에는 제한이 없습니다.The tasks run asynchronously and may complete in any order. 계산이 완료되기 전에 Result 속성에 액세스할 경우 이 속성이 값을 사용할 수 있을 때까지 호출 스레드를 차단합니다.If the Result property is accessed before the computation finishes, the property blocks the calling thread until the value is available.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
        Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(100.0)), 
                                     Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };

        var results = new Double[taskArray.Length];
        Double sum = 0;
        
        for (int i = 0; i < taskArray.Length; i++) {
            results[i] = taskArray[i].Result;
            Console.Write("{0:N1} {1}", results[i], 
                              i == taskArray.Length - 1 ? "= " : "+ ");
            sum += results[i];
        }
        Console.WriteLine("{0:N1}", sum);
   }

   private static Double DoComputation(Double start)
   {
      Double sum = 0;
      for (var value = start; value <= start + 10; value += .1)
         sum += value;

      return sum; 
   }
}
// The example displays the following output:
//        606.0 + 10,605.0 + 100,495.0 = 111,706.0
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Dim taskArray() = { Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
                            Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)), 
                            Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0)) }

        Dim results(taskArray.Length - 1) As Double
        Dim sum As Double
        
        For i As Integer = 0 To taskArray.Length - 1
            results(i) = taskArray(i).Result
            Console.Write("{0:N1} {1}", results(i), 
                              If(i = taskArray.Length - 1, "= ", "+ "))
            sum += results(i)
        Next
        Console.WriteLine("{0:N1}", sum)
   End Sub
   
   Private Function DoComputation(start As Double) As Double
      Dim sum As Double
      For value As Double = start To start + 10 Step .1
         sum += value
      Next
      Return sum 
   End Function
End Module
' The example displays the following output:
'       606.0 + 10,605.0 + 100,495.0 = 111,706.0

자세한 내용은 방법: 작업에서 값 반환을 참조하세요.For more information, see How to: Return a Value from a Task.

람다 식을 사용하여 대리자를 만드는 경우 소스 코드의 해당 지점에서 표시되는 모든 변수에 액세스할 수 있습니다.When you use a lambda expression to create a delegate, you have access to all the variables that are visible at that point in your source code. 그러나 특히 루프 내에서 람다가 기대한 대로 변수를 캡처하지 않는 경우가 있습니다.However, in some cases, most notably within loops, a lambda doesn't capture the variable as expected. 이 경우 람다는 각 반복 후에 변경할 때 값이 아닌 최종 값만 capture합니다.It only captures the final value, not the value as it mutates after each iteration. 다음 예제에서 이 문제를 보여 줍니다.The following example illustrates the problem. CustomData 개체를 인스턴스화하고 루프 카운터를 개체 식별자로 사용하는 람다 식에 루프 카운터를 전달합니다.It passes a loop counter to a lambda expression that instantiates a CustomData object and uses the loop counter as the object's identifier. 이 예제의 출력에서 표시되는 것처럼 각 CustomData 개체에는 동일한 식별자가 있습니다.As the output from the example shows, each CustomData object has an identical identifier.

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

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

public class Example
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in the loop
      // counter. This produces an unexpected result.
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj) => {
                                                 var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks}; 
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum);
                                               },
                                              i );
      }
      Task.WaitAll(taskArray);     
   }
}
// The example displays output like the following:
//       Task #10 created at 635116418427727841 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427727841 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427737842 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

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

Module Example
   Public Sub Main()
      ' Create the task object by using an Action(Of Object) to pass in the loop
      ' counter. This produces an unexpected result.
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} 
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum)
                                              End Sub,
                                              i )
      Next
      Task.WaitAll(taskArray)     
   End Sub
End Module
' The example displays output like the following:
'       Task #10 created at 635116418427727841 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427727841 on thread #3.
'       Task #10 created at 635116418427747843 on thread #3.
'       Task #10 created at 635116418427747843 on thread #3.
'       Task #10 created at 635116418427737842 on thread #4.

생성자를 통해 작업에 상태 개체를 제공하여 각 반복의 값에 액세스할 수 있습니다.You can access the value on each iteration by providing a state object to a task through its constructor. 다음 예제에서는 CustomData 개체를 만들 때 루프 카운터를 사용하여 이전 예제를 수정하여 람다 식에 전달되도록 합니다.The following example modifies the previous example by using the loop counter when creating the CustomData object, which, in turn, is passed to the lambda expression. 예제 출력에서 보듯이 각 CustomData 개체는 이제 개체를 인스턴스화한 시간에 루프 카운터의 값을 기반으로 한 고유 식별자가 있습니다.As the output from the example shows, each CustomData object now has a unique identifier based on the value of the loop counter at the time the object was instantiated.

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

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

public class Example
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in custom data
      // to the Task constructor. This is useful when you need to capture outer variables
      // from within a loop. 
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                  Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum);
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

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

Module Example
   Public Sub Main()
      ' Create the task object by using an Action(Of Object) to pass in custom data
      ' to the Task constructor. This is useful when you need to capture outer variables
      ' from within a loop. 
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum)
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     
   End Sub
End Module
' The example displays output like the following:
'       Task #0 created at 635116412924597583 on thread #3.
'       Task #1 created at 635116412924607584 on thread #4.
'       Task #3 created at 635116412924607584 on thread #4.
'       Task #4 created at 635116412924607584 on thread #4.
'       Task #2 created at 635116412924607584 on thread #3.
'       Task #6 created at 635116412924607584 on thread #3.
'       Task #5 created at 635116412924607584 on thread #4.
'       Task #8 created at 635116412924607584 on thread #4.
'       Task #7 created at 635116412924607584 on thread #3.
'       Task #9 created at 635116412924607584 on thread #4.

이 상태는 작업 대리자에 인수로 전달되며 작업 개체에서 Task.AsyncState 속성을 사용하여 액세스할 수 있습니다.This state is passed as an argument to the task delegate, and it can be accessed from the task object by using the Task.AsyncState property. 다음 예제는 이전 예제를 약간 변형한 예제입니다.The following example is a variation on the previous example. AsyncState 속성을 사용하여 람다 식에 전달된 CustomData 개체에 대한 정보를 표시합니다.It uses the AsyncState property to display information about the CustomData objects passed to the lambda expression.

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

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

public class Example
{
   public static void Main()
   {
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
      foreach (var task in taskArray) {
         var data = task.AsyncState as CustomData;
         if (data != null)
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum);
      }                     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

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

Module Example
   Public Sub Main()
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     

      For Each task In taskArray
         Dim data = TryCast(task.AsyncState, CustomData)
         If data IsNot Nothing Then
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum)
         End If
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task #0 created at 635116451245250515, ran on thread #3, RanToCompletion
'    Task #1 created at 635116451245270515, ran on thread #4, RanToCompletion
'    Task #2 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #3 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #4 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #5 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #6 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #7 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #8 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #9 created at 635116451245270515, ran on thread #3, RanToCompletion

작업 IDTask ID

모든 작업은 애플리케이션 도메인에서 작업을 고유하게 식별하는 정수 ID를 받으며, 이 ID는 Task.Id 속성을 사용하여 액세스할 수 있습니다.Every task receives an integer ID that uniquely identifies it in an application domain and can be accessed by using the Task.Id property. ID는 Visual Studio 디버거의 병렬 스택작업 창에서 작업 정보를 보는 데 유용합니다.The ID is useful for viewing task information in the Visual Studio debugger Parallel Stacks and Tasks windows. ID는 나중에 만들어집니다. 즉, 요청될 때까지는 ID가 만들어지지 않으므로 프로그램이 실행될 때마다 작업 ID가 달라질 수 있습니다.The ID is lazily created, which means that it isn't created until it is requested; therefore, a task may have a different ID every time the program is run. 디버거에서 작업 ID를 보는 방법에 대한 자세한 내용은 작업 창 사용병렬 스택 창 사용을 참조하세요.For more information about how to view task IDs in the debugger, see Using the Tasks Window and Using the Parallel Stacks Window.

작업 생성 옵션Task creation options

작업을 만드는 대부분의 API는 TaskCreationOptions 매개 변수를 사용하는 오버로드를 제공합니다.Most APIs that create tasks provide overloads that accept a TaskCreationOptions parameter. 이러한 옵션 중 하나를 지정하여 스레드 풀에서 작업을 예약하는 방법을 작업 스케줄러에 지시할 수 있습니다.By specifying one of these options, you tell the task scheduler how to schedule the task on the thread pool. 다음 표에서는 다양한 작업 생성 옵션을 보여 줍니다.The following table lists the various task creation options.

TaskCreationOptions 매개 변수 값TaskCreationOptions parameter value 설명Description
None 옵션을 지정하지 않을 경우의 기본값입니다.The default when no option is specified. 스케줄러에서는 스케줄러의 기본 휴리스틱을 사용하여 작업을 예약합니다.The scheduler uses its default heuristics to schedule the task.
PreferFairness 먼저 만들어진 작업은 먼저 실행될 가능성이 높고 나중에 만들어진 작업은 나중에 실행될 가능성이 높게 작업이 예약되도록 지정합니다.Specifies that the task should be scheduled so that tasks created sooner will be more likely to be executed sooner, and tasks created later will be more likely to execute later.
LongRunning 작업이 장기 실행 작업을 나타냄을 지정합니다.Specifies that the task represents a long-running operation.
AttachedToParent 작업을 현재 작업(있는 경우)의 연결된 자식 작업으로 만들어야 함을 지정합니다.Specifies that a task should be created as an attached child of the current task, if one exists. 자세한 내용은 연결된 자식 작업과 분리된 자식 작업을 참조하세요.For more information, see Attached and Detached Child Tasks.
DenyChildAttach 내부 작업이 AttachedToParent 옵션을 지정할 경우 해당 작업이 연결된 자식 작업이 되지 않도록 지정합니다.Specifies that if an inner task specifies the AttachedToParent option, that task will not become an attached child task.
HideScheduler 특정 작업 내에서 TaskFactory.StartNew 또는 Task<TResult>.ContinueWith 같은 메서드를 호출하여 만든 작업을 위한 작업 스케줄러가 이 작업이 실행 중인 스케줄러 대신 기본 스케줄러임을 지정합니다.Specifies that the task scheduler for tasks created by calling methods like TaskFactory.StartNew or Task<TResult>.ContinueWith from within a particular task is the default scheduler instead of the scheduler on which this task is running.

이러한 옵션과 비트 OR 연산을 함께 사용할 수 있습니다.The options may be combined by using a bitwise OR operation. 다음 예제에서는 LongRunningPreferFairness 옵션이 있는 작업을 보여 줍니다.The following example shows a task that has the LongRunning and PreferFairness option.

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

Dim task3 = New Task(Sub() MyLongRunningMethod(),
                        TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()

작업, 스레드 및 문화권Tasks, threads, and culture

각 스레드에는 각각 Thread.CurrentCultureThread.CurrentUICulture 속성으로 정의된 관련 문화권 및 UI 문화권이 있습니다.Each thread has an associated culture and UI culture, which is defined by the Thread.CurrentCulture and Thread.CurrentUICulture properties, respectively. 스레드의 문화권은 형식 지정, 구문 분석, 정렬 및 문자열 비교와 같은 작업에서 사용됩니다.A thread's culture is used in such operations as formatting, parsing, sorting, and string comparison. 스레드의 UI 문화권은 리소스 조회에서 사용됩니다.A thread's UI culture is used in resource lookup. 일반적으로, CultureInfo.DefaultThreadCurrentCultureCultureInfo.DefaultThreadCurrentUICulture 속성을 사용하여 애플리케이션 도메인의 모든 스레드에 대한 기본 문화권을 지정하지 않는 한 스레드의 기본 문화권 및 UI 문화권은 시스템 문화권에 의해 정의됩니다.Ordinarily, unless you specify a default culture for all the threads in an application domain by using the CultureInfo.DefaultThreadCurrentCulture and CultureInfo.DefaultThreadCurrentUICulture properties, the default culture and UI culture of a thread is defined by the system culture. 스레드의 문화권을 명시적으로 설정하고 새 스레드를 시작하는 경우 새 스레드는 호출 스레드의 문화권을 상속하지 않습니다. 대신, 해당 문화권은 기본 시스템 문화권입니다.If you explicitly set a thread's culture and launch a new thread, the new thread does not inherit the culture of the calling thread; instead, its culture is the default system culture. .NET Framework 4.6 이전의 .NET Framework 버전을 대상으로 하는 앱에 대한 작업 기반 프로그래밍 모델은 이 관행을 준수합니다.The task-based programming model for apps that target versions of the .NET Framework prior to .NET Framework 4.6 adhere to this practice.

중요

작업 컨텍스트의 일부인 호출 스레드의 문화권은 .NET Framework 4.6 아래에서 실행되는 앱이 아니라 .NET Framework 4.6을 대상으로 하는 앱에 적용됩니다.Note that the calling thread's culture as part of a task's context applies to apps that target the .NET Framework 4.6, not apps that run under the .NET Framework 4.6. Visual Studio에서 새 프로젝트 대화 상자 위쪽의 드롭다운 목록에서 버전을 선택하여 프로젝트를 만들 때 특정 버전의 .NET Framework를 대상으로 지정할 수 있으며, Visual Studio 외부에서는 TargetFrameworkAttribute 특성을 사용할 수 있습니다.You can target a particular version of the .NET Framework when you create your project in Visual Studio by selecting that version from the dropdown list at the top of the New Project dialog box, or outside of Visual Studio you can use the TargetFrameworkAttribute attribute. .NET Framework 4.6 이전의 .NET Framework 버전을 대상으로 하거나 특정 버전의 .NET Framework를 대상으로 하는 앱의 경우 작업의 문화권은 작업이 실행되는 스레드의 문화권에 의해 계속 결정됩니다.For apps that target versions of the .NET Framework prior to the .NET Framework 4.6, or that do not target a specific version of the .NET Framework, a task's culture continues to be determined by the culture of the thread on which it runs.

.NET Framework 4.6을 대상으로 하는 앱부터 작업이 스레드 풀 스레드에서 비동기적으로 실행되는 경우에도 호출 스레드의 문화권이 각 작업에 상속됩니다.Starting with apps that target the .NET Framework 4.6, the calling thread's culture is inherited by each task, even if the task runs asynchronously on a thread pool thread.

다음 예제에서는 간단한 설명을 제공합니다.The following example provides a simple illustration. TargetFrameworkAttribute 특성을 사용하여 .NET Framework 4.6을를 대상으로 지정하고 앱의 현재 문화권을 프랑스어(프랑스) 또는 프랑스어(프랑스)가 이미 현재 문화권인 경우 영어(미국)로 변경합니다.It uses the TargetFrameworkAttribute attribute to target the .NET Framework 4.6 and changes the app's current culture to either French (France) or, if French (France) is already the current culture, English (United States). 그런 다음 새 문화권의 통화 값으로 형식이 지정된 일부 숫자를 반환하는 formatDelegate라는 대리자를 호출합니다.It then invokes a delegate named formatDelegate that returns some numbers formatted as currency values in the new culture. 호출 스레드의 문화권이 비동기 작업에 상속되므로 작업으로서 대리자가 동기적 또는 비동기적인지에 관계없이 예상 결과를 반환합니다.Note that whether the delegate as a task either synchronously or asynchronously, it returns the expected result because the culture of the calling thread is inherited by the asynchronous task.

using System;
using System.Globalization;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;

[assembly:TargetFramework(".NETFramework,Version=v4.6")]

public class Example
{
   
   public static void Main()
   {
       decimal[] values = { 163025412.32m, 18905365.59m };
       string formatString = "C2";
       Func<String> formatDelegate = () => { string output = String.Format("Formatting using the {0} culture on thread {1}.\n",
                                                                           CultureInfo.CurrentCulture.Name,
                                                                           Thread.CurrentThread.ManagedThreadId);
                                             foreach (var value in values)
                                                output += String.Format("{0}   ", value.ToString(formatString));
                                                   
                                             output += Environment.NewLine;
                                             return output;
                                           };
       
       Console.WriteLine("The example is running on thread {0}", 
                         Thread.CurrentThread.ManagedThreadId);
       // Make the current culture different from the system culture.
       Console.WriteLine("The current culture is {0}", 
                         CultureInfo.CurrentCulture.Name);
       if (CultureInfo.CurrentCulture.Name == "fr-FR")
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
       else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

       Console.WriteLine("Changed the current culture to {0}.\n",
                         CultureInfo.CurrentCulture.Name);
       
       // Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:");
       Console.WriteLine(formatDelegate());
       
       // Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:"); 
       var t1 = Task.Run(formatDelegate);
       Console.WriteLine(t1.Result);
       
       Console.WriteLine("Executing a task synchronously:");
       var t2 = new Task<String>(formatDelegate); 
       t2.RunSynchronously();
       Console.WriteLine(t2.Result);
   }
}
// The example displays the following output:
//         The example is running on thread 1
//         The current culture is en-US
//         Changed the current culture to fr-FR.
//
//         Executing the delegate synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task asynchronously:
//         Formatting using the fr-FR culture on thread 3.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
// If the TargetFrameworkAttribute statement is removed, the example
// displays the following output:
//          The example is running on thread 1
//          The current culture is en-US
//          Changed the current culture to fr-FR.
//
//          Executing the delegate synchronously:
//          Formatting using the fr-FR culture on thread 1.
//          163 025 412,32 €   18 905 365,59 €
//
//          Executing a task asynchronously:
//          Formatting using the en-US culture on thread 3.
//          $163,025,412.32   $18,905,365.59
//
//          Executing a task synchronously:
//          Formatting using the fr-FR culture on thread 1.
//          163 025 412,32 €   18 905 365,59 €
Imports System.Globalization
Imports System.Runtime.Versioning
Imports System.Threading
Imports System.Threading.Tasks

<Assembly:TargetFramework(".NETFramework,Version=v4.6")>

Module Example
   Public Sub Main()
       Dim values() As Decimal = { 163025412.32d, 18905365.59d }
       Dim formatString As String = "C2"
       Dim formatDelegate As Func(Of String) = Function()
                                                  Dim output As String = String.Format("Formatting using the {0} culture on thread {1}.",
                                                                                       CultureInfo.CurrentCulture.Name,
                                                                                       Thread.CurrentThread.ManagedThreadId)
                                                  output += Environment.NewLine
                                                  For Each value In values
                                                     output += String.Format("{0}   ", value.ToString(formatString))
                                                  Next 
                                                  output += Environment.NewLine
                                                  Return output
                                               End Function
       
       Console.WriteLine("The example is running on thread {0}", 
                         Thread.CurrentThread.ManagedThreadId)
       ' Make the current culture different from the system culture.
       Console.WriteLine("The current culture is {0}", 
                         CultureInfo.CurrentCulture.Name)
       If CultureInfo.CurrentCulture.Name = "fr-FR" Then
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US")
       Else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR")
       End If
       Console.WriteLine("Changed the current culture to {0}.",
                         CultureInfo.CurrentCulture.Name)
       Console.WriteLine()                  
       
       ' Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:")
       Console.WriteLine(formatDelegate())
       
       ' Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:") 
       Dim t1 = Task.Run(formatDelegate)
       Console.WriteLine(t1.Result)
       
       Console.WriteLine("Executing a task synchronously:")
       Dim t2 = New Task(Of String)(formatDelegate) 
       t2.RunSynchronously()
       Console.WriteLine(t2.Result)
   End Sub
End Module
' The example displays the following output:
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting Imports the fr-FR culture on thread 3.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
' If the TargetFrameworkAttribute statement is removed, the example
' displays the following output:
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting using the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting using the en-US culture on thread 3.
'          $163,025,412.32   $18,905,365.59
'
'          Executing a task synchronously:
'          Formatting using the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €

Visual Studio를 사용할 경우 TargetFrameworkAttribute 특성을 생략하고 새 프로젝트 대화 상자에서 프로젝트를 만들 때 대신 .NET Framework 4.6을 대상으로 선택할 수 있습니다.If you are using Visual Studio, you can omit the TargetFrameworkAttribute attribute and instead select the .NET Framework 4.6 as the target when you create the project in the New Project dialog.

.NET Framework 4.6 이전의 .NET Framework 버전을 대상으로 하는 앱의 동작을 반영하는 출력의 경우 소스 코드에서 TargetFrameworkAttribute 특성을 제거합니다.For output that reflects the behavior of apps the target versions of the .NET Framework prior to .NET Framework 4.6, remove the TargetFrameworkAttribute attribute from the source code. 출력은 호출 스레드의 문화권이 아닌 기본 시스템 문화권의 형식 지정 규칙을 반영합니다.The output will reflect the formatting conventions of the default system culture, not the culture of the calling thread.

비동기 작업 및 문화권에 대한 자세한 내용은 CultureInfo 항목의 "문화권 및 비동기 작업 기반 작업" 섹션을 참조하세요.For more information on asynchronous tasks and culture, see the "Culture and asynchronous task-based operations" section in the CultureInfo topic.

작업 연속 만들기Creating task continuations

Task.ContinueWithTask<TResult>.ContinueWith 메서드를 사용하면 ‘선행 작업’이 완료될 때 시작할 작업을 지정할 수 있습니다. The Task.ContinueWith and Task<TResult>.ContinueWith methods let you specify a task to start when the antecedent task finishes. 연속 작업의 대리자가 선행 작업으로 참조를 전달하여 Task<TResult>.Result 속성의 값을 검색하여 선행 작업의 상태를 검사할 수 있으며 선행 작업의 출력을 연속 작업의 입력으로 사용할 수 있습니다.The delegate of the continuation task is passed a reference to the antecedent task so that it can examine the antecedent task's status and, by retrieving the value of the Task<TResult>.Result property, can use the output of the antecedent as input for the continuation.

다음 예제에서는 getData 작업이 TaskFactory.StartNew<TResult>(Func<TResult>) 메서드에 대한 호출로 시작됩니다.In the following example, the getData task is started by a call to the TaskFactory.StartNew<TResult>(Func<TResult>) method. processData작업은 getData가 완료되면 자동으로 시작되고 displayDataprocessData가 완료되면 시작됩니다.The processData task is started automatically when getData finishes, and displayData is started when processData finishes. getDataprocessData 작업의 getData 속성을 통해 Task<TResult>.Result 작업에 액세스할 수 있는 정수 배열을 생성합니다.getData produces an integer array, which is accessible to the processData task through the getData task's Task<TResult>.Result property. processData 작업은 해당 배열을 처리하고 Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) 메서드로 전달된 람다 식의 반환 형식에서 유추한 형식을 갖는 결과를 반환합니다.The processData task processes that array and returns a result whose type is inferred from the return type of the lambda expression passed to the Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) method. displayData 작업은 processData가 완료되면 자동으로 실행되며 Tuple<T1,T2,T3> 람다 식에서 반환한 processData 개체는 displayData 작업의 processData 속성을 통해 Task<TResult>.Result 작업에 액세스할 수 있습니다.The displayData task executes automatically when processData finishes, and the Tuple<T1,T2,T3> object returned by the processData lambda expression is accessible to the displayData task through the processData task's Task<TResult>.Result property. displayData 작업은 processData 작업의 결과를 사용해서 해당 형식이 비슷한 방식으로 유추되고 Result 속성의 프로그램에서 사용할 수 있는 결과를 생성합니다.The displayData task takes the result of the processData task and produces a result whose type is inferred in a similar manner and which is made available to the program in the Result property.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {                         
      var getData = Task.Factory.StartNew(() => { 
                                             Random rnd = new Random(); 
                                             int[] values = new int[100];
                                             for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                values[ctr] = rnd.Next();

                                             return values;
                                          } );  
      var processData = getData.ContinueWith((x) => {
                                                int n = x.Result.Length;
                                                long sum = 0;
                                                double mean;
                                  
                                                for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                                   sum += x.Result[ctr];

                                                mean = sum / (double) n;
                                                return Tuple.Create(n, sum, mean);
                                             } ); 
      var displayData = processData.ContinueWith((x) => {
                                                    return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2, 
                                                                         x.Result.Item3);
                                                 } );                         
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim getData = Task.Factory.StartNew(Function() 
                                             Dim rnd As New Random()
                                             Dim values(99) As Integer
                                             For ctr = 0 To values.GetUpperBound(0)
                                                values(ctr) = rnd.Next()
                                             Next
                                             Return values
                                          End Function)  
      Dim processData = getData.ContinueWith(Function(x)
                                                Dim n As Integer = x.Result.Length
                                                Dim sum As Long
                                                Dim mean As Double
                                  
                                                For ctr = 0 To x.Result.GetUpperBound(0)
                                                   sum += x.Result(ctr)
                                                Next
                                                mean = sum / n
                                                Return Tuple.Create(n, sum, mean)
                                             End Function) 
      Dim displayData = processData.ContinueWith(Function(x)
                                                    Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2, 
                                                                         x.Result.Item3)
                                                 End Function)                         
      Console.WriteLine(displayData.Result)
   End Sub                        
End Module
' The example displays output like the following:
'   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

Task.ContinueWith는 인스턴스 메서드이므로 각 선행 작업에 대해 Task<TResult> 개체를 인스턴스화하는 대신 메서드를 연속 호출할 수 있습니다.Because Task.ContinueWith is an instance method, you can chain method calls together instead of instantiating a Task<TResult> object for each antecedent task. 다음 예제는 Task.ContinueWith 메서드에 대한 호출도 함께 연결된다는 점을 제외하고 이전 예제와 기능적으로 동일합니다.The following example is functionally identical to the previous example, except that it chains together calls to the Task.ContinueWith method. 일련의 메서드 호출에서 반환되는 Task<TResult> 개체는 최종 연속 작업입니다.Note that the Task<TResult> object returned by the chain of method calls is the final continuation task.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {                         
      var displayData = Task.Factory.StartNew(() => { 
                                                 Random rnd = new Random(); 
                                                 int[] values = new int[100];
                                                 for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                    values[ctr] = rnd.Next();

                                                 return values;
                                              } ).  
                        ContinueWith((x) => {
                                        int n = x.Result.Length;
                                        long sum = 0;
                                        double mean;
                                  
                                        for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                           sum += x.Result[ctr];

                                        mean = sum / (double) n;
                                        return Tuple.Create(n, sum, mean);
                                     } ). 
                        ContinueWith((x) => {
                                        return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2, 
                                                             x.Result.Item3);
                                     } );                         
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim displayData = Task.Factory.StartNew(Function() 
                                                 Dim rnd As New Random()
                                                 Dim values(99) As Integer
                                                 For ctr = 0 To values.GetUpperBound(0)
                                                    values(ctr) = rnd.Next()
                                                 Next
                                                 Return values
                                              End Function). _  
                        ContinueWith(Function(x)
                                        Dim n As Integer = x.Result.Length
                                        Dim sum As Long
                                        Dim mean As Double
                                  
                                        For ctr = 0 To x.Result.GetUpperBound(0)
                                           sum += x.Result(ctr)
                                        Next
                                        mean = sum / n
                                        Return Tuple.Create(n, sum, mean)
                                     End Function). _ 
                        ContinueWith(Function(x)
                                        Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2,  
                                                             x.Result.Item3)
                                     End Function)                         
      Console.WriteLine(displayData.Result)
   End Sub                        
End Module
' The example displays output like the following:
'   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

ContinueWhenAllContinueWhenAny 메서드를 사용하면 여러 작업을 연속적으로 수행할 수 있습니다.The ContinueWhenAll and ContinueWhenAny methods enable you to continue from multiple tasks.

자세한 내용은 연속 작업을 사용하여 작업 연결을 참조하세요.For more information, see Chaining Tasks by Using Continuation Tasks.

분리된 자식 작업 만들기Creating detached child tasks

작업에서 실행되는 사용자 코드를 통해 새 작업이 만들어지지만 AttachedToParent 옵션은 지정되지 않을 경우 새 작업은 어떤 특수한 방법으로도 부모 작업과 동기화되지 않습니다.When user code that is running in a task creates a new task and does not specify the AttachedToParent option, the new task is not synchronized with the parent task in any special way. 이 유형의 동기화되지 않은 작업을 분리된 중첩 작업 또는 분리된 자식 작업이라고 부릅니다.This type of non-synchronized task is called a detached nested task or detached child task. 다음 예제에서는 분리된 상태의 자식 작업을 하나 만드는 작업을 보여 줍니다.The following example shows a task that creates one detached child task.

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.");
// The example displays the following output:
//    Outer task beginning.
//    Outer task completed.
//    Detached task completed.
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.")
' The example displays the following output:
'     Outer task beginning.
'     Outer task completed.
'    Detached child completed.

부모 작업은 분리된 자식 작업이 완료될 때까지 대기하지 않습니다.Note that the parent task does not wait for the detached child task to finish.

자식 작업 만들기Creating child tasks

작업에서 실행되는 사용자 코드를 통해 AttachedToParent 옵션을 사용해서 작업을 만들 경우, 새 작업은 부모 작업의 ‘연결된 자식 작업’으로 알려집니다. When user code that is running in a task creates a task with the AttachedToParent option, the new task is known as a attached child task of the parent task. AttachedToParent 옵션을 사용하면 부모 작업은 암시적으로 모든 연결된 자식 작업이 완료될 때까지 대기하게 되므로 이 옵션을 사용하여 구조적 작업 병렬 처리를 표현할 수 있습니다.You can use the AttachedToParent option to express structured task parallelism, because the parent task implicitly waits for all attached child tasks to finish. 다음 예제에서는 10개의 연결된 자식 작업을 만드는 부모 작업을 보여 줍니다.The following example shows a parent task that creates ten attached child tasks. 예제는 부모 작업이 완료되기를 기다리도록 Task.Wait 메서드를 호출하지만 첨부된 자식 작업이 완료되기를 명시적으로 기다릴 필요가 없습니다.Note that although the example calls the Task.Wait method to wait for the parent task to finish, it does not have to explicitly wait for the attached child tasks to complete.

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

public class Example
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
                      Console.WriteLine("Parent task beginning.");
                      for (int ctr = 0; ctr < 10; ctr++) {
                         int taskNo = ctr;
                         Task.Factory.StartNew((x) => {
                                                  Thread.SpinWait(5000000);
                                                  Console.WriteLine("Attached child #{0} completed.", 
                                                                    x);
                                               },
                                               taskNo, TaskCreationOptions.AttachedToParent);
                      }
                   });

      parent.Wait();
      Console.WriteLine("Parent task completed.");
   }
}
// The example displays output like the following:
//       Parent task beginning.
//       Attached child #9 completed.
//       Attached child #0 completed.
//       Attached child #8 completed.
//       Attached child #1 completed.
//       Attached child #7 completed.
//       Attached child #2 completed.
//       Attached child #6 completed.
//       Attached child #3 completed.
//       Attached child #5 completed.
//       Attached child #4 completed.
//       Parent task completed.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Factory.StartNew(Sub()
                                            Console.WriteLine("Parent task beginning.")
                                            For ctr As Integer = 0 To 9
                                               Dim taskNo As Integer = ctr
                                               Task.Factory.StartNew(Sub(x)
                                                                        Thread.SpinWait(5000000)
                                                                        Console.WriteLine("Attached child #{0} completed.", 
                                                                                          x)
                                                                     End Sub,
                                                                     taskNo, TaskCreationOptions.AttachedToParent)
                                            Next
                                         End Sub)
      parent.Wait()
      Console.WriteLine("Parent task completed.")
   End Sub
End Module   
' The example displays output like the following:
'       Parent task beginning.
'       Attached child #9 completed.
'       Attached child #0 completed.
'       Attached child #8 completed.
'       Attached child #1 completed.
'       Attached child #7 completed.
'       Attached child #2 completed.
'       Attached child #6 completed.
'       Attached child #3 completed.
'       Attached child #5 completed.
'       Attached child #4 completed.
'       Parent task completed.

부모 작업은 TaskCreationOptions.DenyChildAttach 옵션을 사용하여 부모 작업에 다른 작업이 첨부되지 않도록 할 수 있습니다.A parent task can use the TaskCreationOptions.DenyChildAttach option to prevent other tasks from attaching to the parent task. 자세한 내용은 연결된 자식 작업과 분리된 자식 작업을 참조하세요.For more information, see Attached and Detached Child Tasks.

작업 완료 시까지 대기Waiting for tasks to finish

System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<TResult> 형식은 작업이 완료될 때까지 대기할 수 있게 해주는 Task.Wait 메서드의 몇 가지 오버로드를 제공합니다.The System.Threading.Tasks.Task and System.Threading.Tasks.Task<TResult> types provide several overloads of the Task.Wait methods that enable you to wait for a task to finish. 또한 정적 Task.WaitAllTask.WaitAny 메서드의 오버로드를 사용하면 작업 배열의 일부 또는 모두가 완료될 때까지 대기할 수 있습니다.In addition, overloads of the static Task.WaitAll and Task.WaitAny methods let you wait for any or all of an array of tasks to finish.

일반적으로 다음과 같은 경우에 작업을 대기시킵니다.Typically, you would wait for a task for one of these reasons:

  • 주 스레드에서 작업에 의해 계산된 최종 결과를 사용해야 하는 경우The main thread depends on the final result computed by a task.

  • 작업에서 throw될 수 있는 예외를 처리해야 하는 경우You have to handle exceptions that might be thrown from the task.

  • 애플리케이션은 모든 작업이 실행을 완료하기 전에 종료할 수 있습니다.The application may terminate before all tasks have completed execution. 예를 들어, 콘솔 애플리케이션은 Main(애플리케이션 진입점)의 모든 동기 코드가 실행되는 즉시 종료됩니다.For example, console applications will terminate as soon as all synchronous code in Main (the application entry point) has executed.

다음 예제에서는 예외 처리가 포함되지 않은 기본적인 패턴을 보여 줍니다.The following example shows the basic pattern that does not involve exception handling.

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...
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...

예외 처리를 보여 주는 예제는 예외 처리를 참조하세요.For an example that shows exception handling, see Exception Handling.

일부 오버로드에서는 제한 시간을 지정할 수 있으며, 다른 오버로드에서는 프로그래밍 방식으로 또는 사용자 입력에 대한 응답으로 대기 자체를 취소할 수 있도록 추가 CancellationToken을 입력 매개 변수로 사용합니다.Some overloads let you specify a time-out, and others take an additional CancellationToken as an input parameter, so that the wait itself can be canceled either programmatically or in response to user input.

작업을 대기할 때는 암시적으로 TaskCreationOptions.AttachedToParent 옵션의 사용으로 생성된 해당 작업의 모든 자식 작업을 대기합니다.When you wait for a task, you implicitly wait for all children of that task that were created by using the TaskCreationOptions.AttachedToParent option. Task.Wait는 작업이 이미 완료되었는지 여부를 즉시 반환합니다.Task.Wait returns immediately if the task has already completed. 작업에서 발생한 예외는 모두 Task.Wait 메서드에 의해 throw됩니다. 이는 Task.Wait 메서드가 작업 완료 후 호출된 경우에도 해당됩니다.Any exceptions raised by a task will be thrown by a Task.Wait method, even if the Task.Wait method was called after the task completed.

작업 작성Composing tasks

TaskTask<TResult> 클래스는 공통 패턴을 구현하고 C#, Visual Basic 및 F#에서 제공하는 비동기 언어 기능을 더욱 효율적으로 사용하도록 여러 작업을 구성할 수 있는 여러 메서드를 제공합니다.The Task and Task<TResult> classes provide several methods that can help you compose multiple tasks to implement common patterns and to better use the asynchronous language features that are provided by C#, Visual Basic, and F#. 이 단원에서는 WhenAll, WhenAny, DelayFromResult 메서드에 대해 설명합니다.This section describes the WhenAll, WhenAny, Delay, and FromResult methods.

Task.WhenAllTask.WhenAll

Task.WhenAll 메서드는 여러 Task 또는 Task<TResult> 개체가 완료될 때까지 비동기적으로 대기합니다.The Task.WhenAll method asynchronously waits for multiple Task or Task<TResult> objects to finish. 균일하지 않은 작업 집합에 대해 대기할 수 있는 오버로드된 버전을 제공합니다.It provides overloaded versions that enable you to wait for non-uniform sets of tasks. 예를 들어, 여러 TaskTask<TResult> 개체가 한 메서드 호출에서 완료되기를 기다릴 수 있습니다.For example, you can wait for multiple Task and Task<TResult> objects to complete from one method call.

Task.WhenAnyTask.WhenAny

Task.WhenAny 메서드는 여러 Task 또는 Task<TResult> 개체 중 하나가 완료될 때까지 비동기적으로 대기합니다.The Task.WhenAny method asynchronously waits for one of multiple Task or Task<TResult> objects to finish. Task.WhenAll 메서드에서와 마찬가지로 이 메서드는 균일하지 않은 작업 집합에 대해 대기할 수 있는 오버로드된 버전을 제공합니다.As in the Task.WhenAll method, this method provides overloaded versions that enable you to wait for non-uniform sets of tasks. WhenAny 메서드는 다음과 같은 시나리오에서 특히 유용합니다.The WhenAny method is especially useful in the following scenarios.

  • 중복 작업입니다.Redundant operations. 알고리즘 또는 여러 방법으로 수행할 수 있는 작업을 고려합니다.Consider an algorithm or operation that can be performed in many ways. WhenAny 메서드를 사용하여 먼저 완료되는 작업을 선택한 다음 나머지 작업을 취소할 수 있습니다.You can use the WhenAny method to select the operation that finishes first and then cancel the remaining operations.

  • 인터리브 작업입니다.Interleaved operations. 모두 완료되어야 하는 여러 작업을 시작하고 WhenAny 메서드를 사용하여 각 작업이 완료되면 결과를 처리할 수 있습니다.You can start multiple operations that must all finish and use the WhenAny method to process results as each operation finishes. 하나의 작업이 완료되면 하나 이상의 추가 작업을 시작할 수 있습니다.After one operation finishes, you can start one or more additional tasks.

  • 제한된 작업입니다.Throttled operations. WhenAny 메서드를 사용하여 동시 작업 수를 제한하여 이전 시나리오를 확장할 수 있습니다.You can use the WhenAny method to extend the previous scenario by limiting the number of concurrent operations.

  • 만료된 작업입니다.Expired operations. WhenAny 메서드를 사용하여 Delay 메서드에서 반환되는 작업처럼 특정 시간 이후에 완료되는 하나 이상의 작업 중에서 선택할 수 있습니다.You can use the WhenAny method to select between one or more tasks and a task that finishes after a specific time, such as a task that is returned by the Delay method. Delay 메서드는 다음 단원에서 설명됩니다.The Delay method is described in the following section.

Task.DelayTask.Delay

Task.Delay 메서드는 지정된 시간 이후에 완료되는 Task 개체를 생성합니다.The Task.Delay method produces a Task object that finishes after the specified time. 이 메서드를 사용하여 때때로 데이터를 폴링하는 루프를 빌드하고, 제한 시간을 소개하고, 미리 지정된 시간 동안에 사용자의 입력 처리를 지연하는 데 사용할 수 있습니다.You can use this method to build loops that occasionally poll for data, introduce time-outs, delay the handling of user input for a predetermined time, and so on.

Task(T).FromResultTask(T).FromResult

Task.FromResult 메서드를 사용하면 미리 계산된 결과를 갖는 Task<TResult> 개체를 만들 수 있습니다.By using the Task.FromResult method, you can create a Task<TResult> object that holds a pre-computed result. 이 메서드는 Task<TResult> 개체가 반환되는 비동기 작업을 수행하고 해당 Task<TResult> 개체의 결과가 계산되어 있을 때 유용합니다.This method is useful when you perform an asynchronous operation that returns a Task<TResult> object, and the result of that Task<TResult> object is already computed. FromResult를 사용하여 캐시에 저장된 비동기 다운로드 작업 결과를 검색하는 예제는 방법: 미리 계산된 작업 만들기를 참조하세요.For an example that uses FromResult to retrieve the results of asynchronous download operations that are held in a cache, see How to: Create Pre-Computed Tasks.

작업의 예외 처리Handling exceptions in tasks

작업이 하나 이상의 예외를 throw하면 해당 예외가 AggregateException 예외에 래핑됩니다.When a task throws one or more exceptions, the exceptions are wrapped in an AggregateException exception. 이 예외는 작업과 조인하는 스레드에 다시 전파됩니다. 이 스레드는 일반적으로 작업이 완료될 때까지 기다리는 스레드이거나 Result 속성에 액세스하는 스레드입니다.That exception is propagated back to the thread that joins with the task, which is typically the thread that is waiting for the task to finish or the thread that accesses the Result property. 이 동작을 통해 처리되지 않은 예외가 발생할 때마다 기본적으로 프로세스를 종료하도록 하는 .NET Framework 정책을 적용할 수 있습니다.This behavior serves to enforce the .NET Framework policy that all unhandled exceptions by default should terminate the process. 호출 코드는 다음 try/catch 블록을 사용하여 예외를 처리할 수 있습니다.The calling code can handle the exceptions by using any of the following in a try/catch block:

또한 조인하는 스레드에서는 작업이 가비지 수집되기 전에 Exception 속성에 액세스하여 예외를 처리할 수 있습니다.The joining thread can also handle exceptions by accessing the Exception property before the task is garbage-collected. 이 속성에 액세스하면 처리되지 않은 예외로 인해 개체가 종료될 때 프로세스를 종료하는 예외 전파 동작이 트리거되는 것을 방지할 수 있습니다.By accessing this property, you prevent the unhandled exception from triggering the exception propagation behavior that terminates the process when the object is finalized.

예외 및 작업에 대한 자세한 내용은 예외 처리를 참조하세요.For more information about exceptions and tasks, see Exception Handling.

작업 취소Canceling tasks

Task 클래스는 협조적 취소를 지원하며 .NET Framework 4에 소개된 System.Threading.CancellationTokenSourceSystem.Threading.CancellationToken 클래스와 완전히 통합됩니다.The Task class supports cooperative cancellation and is fully integrated with the System.Threading.CancellationTokenSource and System.Threading.CancellationToken classes, which were introduced in the .NET Framework 4. System.Threading.Tasks.Task 클래스의 많은 생성자는 CancellationToken 개체를 입력 매개 변수로 사용합니다.Many of the constructors in the System.Threading.Tasks.Task class take a CancellationToken object as an input parameter. 대부분의 StartNewRun 오버로드는 CancellationToken 매개 변수를 포함합니다.Many of the StartNew and Run overloads also include a CancellationToken parameter.

이 토큰을 만든 다음 나중에 CancellationTokenSource 클래스를 사용하여 취소 요청을 실행할 수 있습니다.You can create the token, and issue the cancellation request at some later time, by using the CancellationTokenSource class. 이 토큰을 Task에 인수로 전달하고, 취소 요청에 대한 응답 작업을 수행하는 사용자 대리자에서도 동일한 토큰을 참조합니다.Pass the token to the Task as an argument, and also reference the same token in your user delegate, which does the work of responding to a cancellation request.

자세한 내용은 작업 취소방법: 작업 및 해당 자식 취소를 참조하세요.For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

TaskFactory 클래스The TaskFactory class

TaskFactory 클래스에서는 작업 및 연속 작업을 만들고 시작하기 위한 몇 가지 일반적인 패턴을 캡슐화하는 정적 메서드를 제공합니다.The TaskFactory class provides static methods that encapsulate some common patterns for creating and starting tasks and continuation tasks.

기본 TaskFactoryTask 클래스 또는 Task<TResult> 클래스에서 정적 속성으로 액세스될 수 있습니다.The default TaskFactory can be accessed as a static property on the Task class or Task<TResult> class. TaskFactory를 직접 인스턴스화하고 CancellationToken, TaskCreationOptions 옵션, TaskContinuationOptions 옵션 또는 TaskScheduler를 포함한 다양한 옵션을 지정할 수도 있습니다.You can also instantiate a TaskFactory directly and specify various options that include a CancellationToken, a TaskCreationOptions option, a TaskContinuationOptions option, or a TaskScheduler. 작업 팩터리를 만들 때 어떤 옵션을 지정하든 작업 팩터리에서 만드는 모든 작업에 해당 옵션이 적용됩니다. 단, Task 열거형을 사용하여 TaskCreationOptions을 만드는 경우는 제외되며, 이때는 해당 작업의 옵션이 작업 팩터리의 옵션을 재정의합니다.Whatever options are specified when you create the task factory will be applied to all tasks that it creates, unless the Task is created by using the TaskCreationOptions enumeration, in which case the task's options override those of the task factory.

대리자가 없는 작업Tasks without delegates

일부 경우에는 Task를 사용하여 사용자 대리자 대신 외부 구성 요소에서 수행되는 비동기 작업을 캡슐화할 수 있습니다.In some cases, you may want to use a Task to encapsulate some asynchronous operation that is performed by an external component instead of your own user delegate. 이 작업이 비동기 프로그래밍 모델인 Begin/End 패턴을 기반으로 하는 경우에는 FromAsync 메서드를 사용할 수 있습니다.If the operation is based on the Asynchronous Programming Model Begin/End pattern, you can use the FromAsync methods. 그러지 않은 경우에는 TaskCompletionSource<TResult> 개체를 사용하여 작업에서 수행할 비동기 작업을 래핑하고 이를 통해 예외 전파 및 연속에 대한 지원과 같은 Task 프로그래밍 기능의 몇 가지 이점을 얻을 수 있습니다.If that is not the case, you can use the TaskCompletionSource<TResult> object to wrap the operation in a task and thereby gain some of the benefits of Task programmability, for example, support for exception propagation and continuations. 자세한 내용은 TaskCompletionSource<TResult>을 참조하세요.For more information, see TaskCompletionSource<TResult>.

사용자 지정 스케줄러Custom schedulers

대부분의 애플리케이션 또는 라이브러리 개발자는 작업이 실행되는 프로세서, 작업과 다른 작업이 동기화되는 방식 또는 System.Threading.ThreadPool에서 작업이 예약되는 방식에는 크게 신경 쓰지 않고,Most application or library developers do not care which processor the task runs on, how it synchronizes its work with other tasks, or how it is scheduled on the System.Threading.ThreadPool. 단지 작업이 호스트 컴퓨터에서 가능한 한 효율적으로 실행되기만을 바랍니다.They only require that it execute as efficiently as possible on the host computer. 예약 세부 사항을 보다 세부적으로 제어해야 하는 경우 작업 병렬 라이브러리를 사용하면 기본 작업 스케줄러의 일부 설정을 구성할 수 있으며 사용자 지정 스케줄러를 지정할 수도 있습니다.If you require more fine-grained control over the scheduling details, the Task Parallel Library lets you configure some settings on the default task scheduler, and even lets you supply a custom scheduler. 자세한 내용은 TaskScheduler을 참조하세요.For more information, see TaskScheduler.

TPL에는 병렬 시나리오와 순차 시나리오 모두에 유용한 새로운 공용 형식이 몇 가지 포함되어 있습니다.The TPL has several new public types that are useful in both parallel and sequential scenarios. 여기에는 스레드로부터 안전하며 속도 및 확장성이 우수한 System.Collections.Concurrent 네임스페이스의 몇 가지 컬렉션 클래스뿐 아니라 특정 종류의 작업 부하에 대해 이전보다 높은 효율성을 제공하는 System.Threading.SemaphoreSystem.Threading.ManualResetEventSlim 등의 새로운 몇 가지 동기화 형식도 포함됩니다.These include several thread-safe, fast and scalable collection classes in the System.Collections.Concurrent namespace, and several new synchronization types, for example, System.Threading.Semaphore and System.Threading.ManualResetEventSlim, which are more efficient than their predecessors for specific kinds of workloads. .NET Framework 4에 새로 추가된 System.Threading.BarrierSystem.Threading.SpinLock 등의 다른 형식은 이전 릴리스에서는 사용할 수 없었던 기능을 제공합니다.Other new types in the .NET Framework 4, for example, System.Threading.Barrier and System.Threading.SpinLock, provide functionality that was not available in earlier releases. 자세한 내용은 병렬 프로그래밍의 데이터 구조를 참조하세요.For more information, see Data Structures for Parallel Programming.

사용자 지정 작업 형식Custom task types

System.Threading.Tasks.Task 또는 System.Threading.Tasks.Task<TResult>에서 상속하지 않는 것이 좋습니다.We recommend that you do not inherit from System.Threading.Tasks.Task or System.Threading.Tasks.Task<TResult>. 대신 AsyncState 속성을 사용하여 추가 데이터 또는 상태를 Task 또는 Task<TResult> 개체에 연결하는 것이 좋습니다.Instead, we recommend that you use the AsyncState property to associate additional data or state with a Task or Task<TResult> object. 확장 메서드를 사용하여 TaskTask<TResult> 클래스의 기능을 확장할 수도 있습니다.You can also use extension methods to extend the functionality of the Task and Task<TResult> classes. 확장 메서드에 대한 자세한 내용은 확장 메서드확장 메서드를 참조하세요.For more information about extension methods, see Extension Methods and Extension Methods.

Task 또는 Task<TResult>에서 상속해야 하는 경우에는 Run, Run 또는 System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult> 또는 System.Threading.Tasks.TaskCompletionSource<TResult> 클래스를 사용하여 사용자 지정 작업 형식의 인스턴스를 만들 수 없습니다. 이러한 메커니즘은 TaskTask<TResult> 개체만 만들기 때문입니다.If you must inherit from Task or Task<TResult>, you cannot use Run, Run, or the System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>, or System.Threading.Tasks.TaskCompletionSource<TResult> classes to create instances of your custom task type because these mechanisms create only Task and Task<TResult> objects. 또한 Task, Task<TResult>, TaskFactoryTaskFactory<TResult>에서 제공하는 작업 연속 메커니즘을 사용하여 사용자 지정 작업의 인스턴스를 만들 수 없습니다. 이 메커니즘에서도 mechanisms also create only TaskTask<TResult> 개체만 만들기 때문입니다.In addition, you cannot use the task continuation mechanisms that are provided by Task, Task<TResult>, TaskFactory, and TaskFactory<TResult> to create instances of your custom task type because these mechanisms also create only Task and Task<TResult> objects.

제목Title 설명Description
연속 작업을 사용하여 작업 연결Chaining Tasks by Using Continuation Tasks 연속 작업이 실행되는 방식을 설명합니다.Describes how continuations work.
연결된 자식 작업 및 분리된 자식 작업Attached and Detached Child Tasks 연결된 자식 작업과 분리된 자식 작업의 차이점을 설명합니다.Describes the difference between attached and detached child tasks.
작업 취소Task Cancellation Task 개체에 기본 제공되는 취소 지원에 대해 설명합니다.Describes the cancellation support that is built into the Task object.
예외 처리Exception Handling 동시 스레드에 대한 예외를 처리하는 방법을 설명합니다.Describes how exceptions on concurrent threads are handled.
방법: Parallel.Invoke를 사용하여 병렬 작업 실행How to: Use Parallel.Invoke to Execute Parallel Operations Invoke를 사용하는 방법을 설명합니다.Describes how to use Invoke.
방법: 작업에서 값 반환How to: Return a Value from a Task 작업을 통해 값을 반환하는 방법을 설명합니다.Describes how to return values from tasks.
방법: 작업 및 해당 자식 취소How to: Cancel a Task and Its Children 작업을 취소하는 방법을 설명합니다.Describes how to cancel tasks.
방법: 미리 계산된 작업 만들기How to: Create Pre-Computed Tasks Task.FromResult 메서드를 사용하여 캐시에 저장된 비동기 다운로드 작업 결과를 검색하는 방법을 설명합니다.Describes how to use the Task.FromResult method to retrieve the results of asynchronous download operations that are held in a cache.
방법: 병렬 작업을 사용하여 이진 트리 트래버스How to: Traverse a Binary Tree with Parallel Tasks 작업을 사용하여 이진 트리를 따라 이동하는 방법을 설명합니다.Describes how to use tasks to traverse a binary tree.
방법: 중첩된 작업 래핑 취소How to: Unwrap a Nested Task Unwrap 확장 메서드를 사용하는 방법을 보여 줍니다.Demonstrates how to use the Unwrap extension method.
데이터 병렬 처리Data Parallelism ForForEach를 사용하여 데이터에 대한 병렬 루프를 만드는 방법을 설명합니다.Describes how to use For and ForEach to create parallel loops over data.
병렬 프로그래밍Parallel Programming .NET Framework 병렬 프로그래밍의 최상위 노드입니다.Top level node for .NET Framework parallel programming.

참고 항목See also