タスク ベースの非同期プログラミング

タスク並列ライブラリ (TPL) は、非同期操作を表すタスクの概念に基づいています。 いくつかの点で、タスクはスレッドまたは ThreadPool 作業項目に似ていますが、高いレベルで抽象化しています。 タスクの並列化とは、1 つ以上の独立したタスクを同時に実行することです。 タスクが提供する主な利点は次の 2 つです。

  • システム リソースをより効率的かつスケーラブルに利用する。

    背後では、タスクは ThreadPool のキューに登録されます。これは、スレッド数を決定および調整し、負荷分散によってスループットを最大化するアルゴリズムで強化されています。 これによりタスクが比較的軽量化されるため、多数のタスクを作成して粒度の高い並列化を実現できます。

  • スレッドまたは作業項目より、プログラムによる制御を詳細に行うことができる。

    タスクおよびタスクを中心に構築されたフレームワークでは、待機、キャンセル、継続、信頼性の高い例外処理、詳細なステータス、カスタムのスケジュール設定などをサポートする豊富な API が用意されています。

この 2 つの理由により、.NET でマルチスレッド、非同期および並列コードを記述する場合に推奨される API は TPL になります。

暗黙的なタスクの作成と実行

Parallel.Invoke メソッドには、任意の数のステートメントを同時に実行する便利な方法が用意されています。 作業項目ごとに Action デリゲートに渡すだけです。 これらのデリゲートを最も簡単に作成するには、ラムダ式を使用します。 ラムダ式では、名前付きメソッドを呼び出したり、コード インラインを指定したりできます。 次の例では、2 つのタスクを同時に作成および開始する基本の Invoke 呼び出しを示しています。 最初のタスクは DoSomeWork という名のメソッドを呼び出すラムダ式によって表され、2 番目のタスクは DoSomeOtherWork という名のメソッドを呼び出すラムダ式によって表されます。

Note

ここでは、ラムダ式を使用して TPL でデリゲートを定義します。 C# または Visual Basic のラムダ式についての情報が必要な場合は、「Lambda Expressions in PLINQ and TPL (PLINQ および TPL のラムダ式)」を参照してください。

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

Note

Task によって背後で作成される Invoke インスタンスの数は、指定するデリゲートの数と等しくなくてもかまいません。 TPL では、特に多数のデリゲートによるさまざまな最適化方法を採用しています。

詳細については、Parallel.Invoke を使用して並列操作を実行する」を参照してください。

タスクの実行をさらに制御する場合、またはタスクから値を返す場合、Task オブジェクトをより明示的に操作する必要があります。

明示的なタスクの作成と実行

値を返さないタスクは、System.Threading.Tasks.Task クラスによって表されます。 値を返すタスクは、System.Threading.Tasks.Task<TResult> から継承された Task クラスで表されます。 タスク オブジェクトはインフラストラクチャの詳細を処理し、タスクの有効期間内に呼び出し元のスレッドからアクセスできるメソッドとプロパティを提供します。 たとえば、タスクの Status プロパティに任意のタイミングでアクセスして、タスクが開始されたか、完了まで実行されたか、取り消されたか、または例外がスローされたかどうかを確認できます。 状態は、TaskStatus 列挙型によって表されます。

タスクを作成するときは、タスクが実行するコードをカプセル化するユーザー デリゲートを指定します。 このデリゲートは名前付きデリゲート、匿名メソッド、またはラムダ式として表すことができます。 ラムダ式には、次の例で示すような名前付きメソッドへの呼び出しを含めることができます。 この例は Task.Wait メソッドの呼び出しを含み、コンソール モードのアプリケーションが終了する前にタスクの実行が完了するようにしていることに注意してください。

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 メソッドを使用して、一度の操作でタスクを作成および開始することもできます。 タスクを管理するために、Run メソッドでは、現在のスレッドに関連付けられているタスク スケジューラーに関係なく、既定のタスク スケジューラーを使用します。 Run メソッドは、タスクの作成とスケジュールの詳細な制御が必要ない場合に、タスクを作成および開始するために適しています。

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 メソッドを使用して、一度の操作でタスクを作成および開始することもできます。 次の例に示すように、作成とスケジュール設定を分ける必要がない場合、追加のタスク作成オプションまたは特定のスケジューラを使う必要がある場合、または Task.AsyncState プロパティを使用して取得できるタスクに追加の状態を渡す必要がある場合は、このメソッドを使用します。

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

Task および Task<TResult> はそれぞれ、Factory の既定のインスタンスを返す、静的な TaskFactory プロパティを公開するので、メソッドを Task.Factory.StartNew() として呼び出すことができます。 また、次の例のタスクは System.Threading.Tasks.Task<TResult> 型であるため、それぞれのタスクは計算の結果を格納するパブリックな Task<TResult>.Result プロパティを持ちます。 タスクは非同期に実行され、任意の順序で完了されることがあります。 計算が終了する前に Result プロパティにアクセスした場合、このプロパティは値が使用可能な状態になるまで呼び出しスレッドをブロックします。

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

詳細については、タスクから値を返す」を参照してください。

ラムダ式を使用してデリゲートを作成すると、ソース コード内の該当ポイントで参照できるすべての変数にアクセスできます。 ただし、特にループ内では、ラムダによって変数が予想どおりにキャプチャされない場合があります。 キャプチャされるのは変数の参照のみであり、各反復処理後に変更された値はキャプチャされません。 この問題を説明する例を次に示します。 これは CustomData オブジェクトをインスタンス化するラムダ式にループ カウンターを渡し、オブジェクトの識別子としてループ カウンターを使用します。 この例の出力結果が示すように、CustomData の各オブジェクトは同じ識別子を持ちます。

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.

反復処理が実行されるたびに値にアクセスできるようにするには、コンストラクターによって状態オブジェクトをタスクに提供します。 次の例では、ラムダ式に渡される CustomData オブジェクトを作成するときに、ループ カウンターを使用して前の例を変更しています。 この例の出力結果が示すように、CustomData の各オブジェクトは、オブジェクトがインスタンス化されたときのループ カウンターの値に基づいて、一意の識別子を持ちます。

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 プロパティを使用することで、タスク オブジェクトから状態にアクセスできます。 次の例は、前の例を変更したものです。 AsyncState プロパティを使用して、ラムダ式に渡される CustomData オブジェクトに関する情報を表示します。

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

タスク ID

各タスクは、アプリケーション ドメイン内で一意に識別され、Task.Id プロパティを使用してアクセスできる整数の ID を受け取ります。 この ID は、Visual Studio デバッガーの [並列スタック] ウィンドウおよび [タスク] ウィンドウでタスク情報を確認する場合に役立ちます。 この ID は ID が要求されるまでは作成されません。したがって、タスクの ID はプログラムが実行されるたびに異なる場合があります。 デバッガーでタスク ID を表示する方法の詳細については、「[タスク] ウィンドウの使用」と「[並列スタック] ウィンドウの使用」を参照してください。

タスクの作成オプション

タスクを作成するほとんどの API には、TaskCreationOptions パラメーターを受け入れるオーバーロードが用意されています。 これらのオプションを 1 つ以上指定すると、タスク スケジューラにスレッド プール上のタスクをスケジュールする方法を指定できます。 ビット OR 演算を使用して、オプションを組み合わせることもできます。

次の例は、LongRunning および PreferFairness オプションが指定されたタスクを示しています。

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

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

タスク、スレッド、およびカルチャ

各スレッドには、それぞれのカルチャが Thread.CurrentCulture および Thread.CurrentUICulture プロパティで定義された、カルチャと UI カルチャが関連付けられています。 スレッドのカルチャは、書式設定、解析、並べ替え、文字列比較などの操作で使用されます。 スレッドの UI カルチャはリソースの検索で使用されます。

CultureInfo.DefaultThreadCurrentCulture および CultureInfo.DefaultThreadCurrentUICulture プロパティを使用してアプリケーション ドメイン内のすべてのスレッドに既定のカルチャを指定していない限り、スレッドの既定のカルチャと UI カルチャはシステム カルチャで定義されます。 スレッドのカルチャを明示的に設定して新しいスレッドを開始すると、新しいスレッドは呼び出し元スレッドのカルチャを継承せず、既定のシステム カルチャがそのカルチャとして使用されます。 ただし、タスクベースのプログラミングでは、タスクが別のスレッドで非同期で実行される場合でも、呼び出し元スレッドのカルチャが使用されます。

簡単な例を次に示します。 アプリの現在のカルチャがフランス語 (フランス) に変更されます (現在のカルチャがフランス語 (フランス) である場合、英語 (米国) に変更されます)。 次に、変更後のカルチャの通貨値として書式設定された数値を返す formatDelegate という名前のデリゲートを呼び出します。 デリゲートがタスクによって同期的にまたは非同期的に呼び出されるかどうかに関わらず、タスクにより呼び出し元スレッドのカルチャが使用されます。

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

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 €
Imports System.Globalization
Imports System.Threading

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 €

Note

.NET Framework 4.6 より前の .NET Framework のバージョンでは、タスクのカルチャは、"呼び出し元スレッド" のカルチャではなく、"実行されている" スレッドのそれによって決定されていました。 これは非同期タスクの場合、タスクが使用するカルチャは、呼び出し元スレッドのカルチャと異なる可能性があることを意味します。

非同期タスクとカルチャの詳細については、「CultureInfo」トピックの「カルチャおよび非同期タスク ベースの操作」を参照してください。

タスクの継続の作成

Task.ContinueWith メソッドおよび Task<TResult>.ContinueWith メソッドで、"継続元タスク" が終了したときに開始されるタスクを指定できます。 継続タスクのデリゲートは継続元タスクへの参照を渡し、継続元タスクのステータスを調査できるようにし、また Task<TResult>.Result プロパティの値を取得して、継続元の出力を継続への入力として使用できるようにします。

次の例では、getData タスクは TaskFactory.StartNew<TResult>(Func<TResult>) メソッドの呼び出しによって開始されます。 processData タスクは getData が終了したときに自動的に開始され、displayDataprocessData が終了したときに開始されます。 getData は、processData タスクの getData プロパティを使用して Task<TResult>.Result タスクがアクセス可能な、整数の配列を生成します。 processData タスクはその配列を処理し、Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) メソッドに渡されるラムダ式の戻り値の型から推論される型を持つ結果を返します。 displayData タスクは、processData が終了したときに自動的に実行され、Tuple<T1,T2,T3> ラムダ式が返した processData オブジェクトは、displayData タスクの processData プロパティを使用して、Task<TResult>.Result タスクからアクセス可能です。 displayData タスクは processData タスクから結果を受け取り、同様の方法を使用して (プログラムで使用できるようになったと) 推論される型を持つ結果を Result プロパティで生成します。

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> のオブジェクトをそれぞれの継続元タスクにインスタンス化する代わりに、メソッド呼び出しを連結することができます。 次の例は前の例と機能的には同じものですが、呼び出しを Task.ContinueWith メソッドに連結している点が異なります。 メソッドの呼び出しチェーンによって返される Task<TResult> オブジェクトが最終的な継続タスクであることに注意してください。

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

ContinueWhenAll メソッドおよび ContinueWhenAny メソッドを使用すると、複数のタスクから継続できます。

詳細については、「継続タスクを使用したタスクの連結」を参照してください。

デタッチされた子タスクの作成

タスクで実行中のユーザー コードで新しいタスクを作成し、AttachedToParent オプションを指定しない場合、新しいタスクはどのような方法でも親タスクとは同期されません。 非同期タスクのこの型は、デタッチされた入れ子のタスク、またはデタッチされた子タスクと呼ばれます。 次の例は、デタッチされた子タスクを 1 つ作成するタスクを示しています。

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.

親タスクはデタッチされた子タスクの終了を待機しないことに注意してください。

子タスクの作成

タスクで実行中のユーザー コードで AttachedToParent オプションを使用してタスクが作成されると、その新しいタスクは、親タスクに "アタッチされた子タスク" になります。 AttachedToParent オプションを使用すると、構成されたタスクの並列化を表現できます。親タスクは、すべてのアタッチされた子タスクが終了するのを暗黙的に待機するためです。 次の例は、アタッチされた子タスクを 10 個作成する親タスクを示しています。 この例は Task.Wait メソッドを呼び出して親タスクの完了を待機しているが、アタッチされた子タスクの完了を明示的には待機する必要がないことに注意してください。

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 オプションを使用して、他のタスクが親タスクにアタッチすることを防ぐことができます。 詳細については、「アタッチされた子タスクとデタッチされた子タスク」を参照してください。

タスクの完了を待機する

System.Threading.Tasks.Task 型と System.Threading.Tasks.Task<TResult> 型には、タスクが終了するまで待機できる Task.Wait メソッドのオーバーロードがいくつか用意されています。 さらに、静的な Task.WaitAll メソッドおよび Task.WaitAny メソッドのオーバーロードにより、一部またはすべてのタスクの配列が終了するまで待機できます。

通常は、タスクを待機するのは次のいずれかの場合です。

  • メイン スレッドが、タスクで計算される最終的な結果に依存する。

  • タスクからスローされる可能性のある例外を処理する必要がある。

  • アプリケーションは、すべてのタスクが実行を完了する前に終了する場合があります。 たとえば、コンソール アプリケーションは Main (アプリケーションのエントリ ポイント) のすべての同期コードが実行されると、すぐに終了します。

次の例は、例外処理を含まない基本的なパターンを示しています。

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

例外処理を示す例については、例外処理に関する記事を参照してください。

タイムアウトを指定できるオーバーロードおよび別の CancellationToken を入力パラメーターとして受け取るオーバーロードの中には、プログラムによって、またはユーザーの入力に応答して待機自体を取り消すことができるものがあります。

タスクを待機する場合は、TaskCreationOptions.AttachedToParent オプションを使用して作成されたタスクのすべての子タスクを暗黙的に待機します。 タスクが既に完了している場合、直ちに Task.Wait が返されます。 タスクで発生した例外は、Task.Wait メソッドがタスクの完了後に呼び出された場合でも Task.Wait メソッドによってスローされます。

タスクを構成する

TaskTask<TResult> クラスは複数のメソッドを提供し、共通のパターンを実装したり、C#、Visual Basic、F# によって提供される非同期言語機能を使うために、複数のタスクを構成したりするのに役立ちます。 このセクションでは WhenAllWhenAnyDelay および FromResult メソッドについて説明します。

Task.WhenAll

Task.WhenAll メソッドは、複数の Task または Task<TResult> オブジェクトが終了するのを、非同期的に待機します。 提供されるオーバーロード バージョンにより、不均一なタスクのセットを待機することができます。 たとえば、1 回のメソッド呼び出しで完了する、複数の Task および Task<TResult> オブジェクトを待機できます。

Task.WhenAny

Task.WhenAny メソッドは、複数の Task の 1 つ、または Task<TResult> オブジェクトが終了するのを、非同期的に待機します。 Task.WhenAll メソッドと同様に、このメソッドはオーバーロード バージョンを提供し、これによって不均一なタスクのセットを待機することができます。 WhenAny メソッドは、特に次のシナリオに役立ちます。

  • 重複した操作。 多くの方法で実行できるアルゴリズムまたは操作を検討してください。 WhenAny メソッドを使用すると、最初の操作を完了して残りの操作を取り消すように、操作を選択できます。

  • インタリーブされた操作。 複数の操作を開始して、それらの操作のすべてが完了し、各操作が完了したら WhenAny メソッドを使って結果を処理できるようにします。 1 つの操作が完了したら、1 つ以上の追加タスクを開始できます。

  • 制限された操作。 WhenAny メソッドを使用して、前のシナリオを拡張し、同時操作の数を制限することができます。

  • 有効期限切れの操作。 WhenAny メソッドを使うと、1 つ以上のタスクと特定の時間後に終了する 1 つのタスクから選択できます。たとえば、Delay メソッドが返すタスクなどです。 Delay メソッドについては、次のセクションで説明します。

Task.Delay

Task.Delay メソッドは、指定時間後に終了する Task オブジェクトを生成します。 このメソッドを使用すると、データのポーリングをときどき行うループをビルドしたり、タイムアウトを使用したり、ユーザー入力の処理を遅延させたりすることができます。

Task(T).FromResult

Task.FromResult メソッドを使用すると、あらかじめ計算された結果を保持する Task<TResult> オブジェクトを作成できます。 このメソッドは Task<TResult> オブジェクトの結果があらかじめ計算されている Task<TResult> オブジェクトを返す、非同期操作を実行する場合に便利です。 キャッシュに保持されている非同期ダウンロード操作の結果を取得する FromResult の使用例の詳細については、「方法:事前計算済みのタスクを作成する」を参照してください。

タスクでの例外処理

タスクで 1 つ以上の例外がスローされると、例外は AggregateException 例外でラップされます。 例外はタスクに連結されたスレッドに反映されます。通常は、タスクの終了を待機しているスレッドか、または Result プロパティにアクセスするスレッドです。 この動作では、.NET Framework ポリシーが適用され、既定ではすべてのハンドルされない例外によってプロセスが終了されます。 呼び出し元のコードは try/catch ブロックで、次のいずれかを使用して、例外を処理できます。

連結しているスレッドでも、タスクがガベージ コレクトされる前に Exception プロパティにアクセスすることで例外を処理できます。 このプロパティにアクセスすると、ハンドルされない例外が、オブジェクトが最終処理されたときにプロセスを終了する例外の反映動作をトリガーしないようにできます。

例外とタスクの詳細については、例外処理に関する記事を参照してください。

タスクの取り消し

Task クラスは他の処理と連携したキャンセル処理をサポートしており、.NET Framework 4 で導入された System.Threading.CancellationTokenSource クラスおよび System.Threading.CancellationToken クラスと完全に統合されています。 System.Threading.Tasks.Task クラスの多くのコンストラクターは、CancellationToken オブジェクトを入力パラメーターとして受け取ります。 StartNew および Run オーバーロードの多くも、CancellationToken パラメーターを含みます。

CancellationTokenSource クラスを使用すると、トークンを作成し、後でキャンセル要求を発行できます。 このトークンを Task に引数として渡し、同じトークンをキャンセル要求に応答するユーザー デリゲートで参照します。

詳細については、「タスクのキャンセル」と「方法:タスクとその子を取り消す」を参照してください。

TaskFactory クラス

TaskFactory クラスには、タスクおよび継続タスクの作成と開始について、一般的なパターンをカプセル化する静的メソッドが用意されています。

既定の TaskFactory へは、Task クラスまたは Task<TResult> クラス上の静的なプロパティとしてアクセスできます。 TaskFactory を直接インスタンス化し、さまざまなオプションを指定することもできます。たとえば、CancellationTokenTaskCreationOptions オプション、TaskContinuationOptions オプション、TaskScheduler などです。 タスク ファクトリを作成するときに指定されるオプションは、タスク ファクトリで作成したすべてのタスクに適用されます。ただし、TaskTaskCreationOptions 列挙型を使用して作成された場合は例外で、タスクのオプションによってタスク ファクトリのオプションがオーバーライドされます。

デリゲートなしのタスク

Task を使用して、固有のユーザー デリゲートではなく外部コンポーネントによって実行される非同期操作をカプセル化する場合があります。 操作が非同期プログラミング モデルの Begin/End パターンに基づいている場合、FromAsync メソッドを使用できます。 そうでない場合は、TaskCompletionSource<TResult> オブジェクトを使用して、タスク内の操作をラップして、Task を外部からプログラミング可能にする利点を活用できます。たとえば、例外の反映および継続のサポートです。 詳細については、「TaskCompletionSource<TResult>」を参照してください。

カスタム スケジューラ

アプリケーションまたはライブラリのほとんどの開発者は、タスクを実行するプロセッサ、他のタスクと動作を同期する方法、System.Threading.ThreadPool でスケジュールする方法などについては気にしません。 気にするのは、ホスト コンピューター上でできるだけ効率的に実行することだけです。 スケジュールの詳細についてより詳細に制御する必要がある場合、タスク並列ライブラリでは、既定のタスク スケジューラの設定を構成でき、さらにカスタム スケジューラを利用することもできます。 詳細については、「TaskScheduler」を参照してください。

TPL には、並列のシナリオおよび順次的なシナリオの両方に役立つ複数の新しいパブリック型があります。 これらの型には、System.Collections.Concurrent 名前空間における、スレッド セーフで、高速、スケーラブルなコレクション クラス、および新しい同期の型が含まれます。たとえば、System.Threading.Semaphore および System.Threading.ManualResetEventSlim は、特定の種類の作業負荷に関しては、先行タスクより効率的です。 その他の .NET Framework 4 の新しい型には、System.Threading.BarrierSystem.Threading.SpinLock があり、以前のリリースでは利用できなかった機能が用意されています。 詳細については、「並列プログラミング向けのデータ構造」を参照してください。

カスタムのタスクの型

System.Threading.Tasks.Task または System.Threading.Tasks.Task<TResult> から継承しないことをお勧めします。 代わりに、AsyncState プロパティを使用して、追加のデータまたは状態を Task オブジェクトまたは Task<TResult> オブジェクトに関連付けることをおすすめします。 拡張メソッドを使用して、Task クラスおよび Task<TResult> クラスの機能を拡張することもできます。 拡張メソッドの詳細については、拡張メソッド (C# プログラミングガイド)拡張メソッド (Visual Basic) に関する記事を参照してください。

Task または Task<TResult> から継承する必要がある場合、RunSystem.Threading.Tasks.TaskFactorySystem.Threading.Tasks.TaskFactory<TResult>System.Threading.Tasks.TaskCompletionSource<TResult> の各クラスを使用して、カスタムのタスクの型のインスタンスを作成することはできません。これらのクラスで作成されるのは、Task および Task<TResult> オブジェクトだけであるためです。 また、TaskTask<TResult>TaskFactory、および TaskFactory<TResult> で提供されるタスク継続機構でも、Task オブジェクトと Task<TResult> オブジェクトしか作成されないため、これらの機構を使用してカスタムのタスクの型のインスタンスを作成することはできません。

Title 説明
継続タスクを使用したタスクの連結 継続の機能について説明します。
アタッチされた子タスクとデタッチされた子タスク アタッチされた子タスクとデタッチされた子タスクの違いについて説明します。
タスクのキャンセル Task オブジェクトに組み込まれているキャンセルのサポートについて説明します。
例外処理 同時実行スレッド上の例外を処理する方法について説明します。
方法: Parallel.Invoke を使用して並列操作を実行する Invoke の使用方法について説明します。
方法: タスクから値を返す タスクから値を返す方法について説明します。
方法: タスクとその子を取り消す タスクを取り消す方法について説明します。
方法: 事前計算済みのタスクを作成する キャッシュに保持されている非同期ダウンロード操作の結果を取得する Task.FromResult メソッドの使用例の詳細について説明します。
方法: 並列タスクでバイナリ ツリーを走査する タスクを使用してバイナリ ツリーを走査する方法について説明します。
方法: 入れ子のタスクのラップを解除する Unwrap 拡張メソッドの使用方法を説明します。
データの並列化 For および ForEach を使用してデータを対象に並列ループを作成する方法について説明しています。
並列プログラミング .NET Framework 並列プログラミングのトップ レベル ノード。

関連項目