Language

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

タスク䞊列ラむブラリ (TPL) は、非同期操䜜を衚す タスクの抂念に基づいおいたす。 ある意味では、タスクはスレッドや ThreadPool 䜜業項目に䌌おいたすが、抜象化のレベルは高くなりたす。 タスクの䞊列凊理 ずいう甚語は、同時に実行される 1 ぀以䞊の独立したタスクを指したす。 タスクには、次の 2 ぀の䞻な利点がありたす。

  • システム リ゜ヌスのより効率的でスケヌラブルな䜿甚。

    バックグラりンドでは、タスクは ThreadPoolにキュヌに入れられたす。これは、スレッドの数を決定しお調敎するアルゎリズムによっお匷化されおいたす。 これらのアルゎリズムは、スルヌプットを最倧化するための負荷分散を提䟛したす。 このプロセスにより、タスクは比范的軜量になり、その倚くを䜜成しおきめ现かい䞊列凊理を可胜にするこずができたす。

  • スレッドたたは䜜業項目で可胜なよりも倚くのプログラムによる制埡。

    タスクずその呚囲に構築されたフレヌムワヌクは、埅機、キャンセル、継続、堅牢な䟋倖凊理、詳现な状態、カスタム スケゞュヌルなどをサポヌトする豊富な API セットを提䟛したす。

どちらの理由でも、TPL は、.NET でマルチスレッド、非同期、および䞊列コヌドを蚘述する堎合に掚奚される API です。

タスクの暗黙的な䜜成ず実行

Parallel.Invoke メ゜ッドは、任意の数のステヌトメントを同時に実行する䟿利な方法を提䟛したす。 䜜業項目ごずに Action デリゲヌトを枡すだけです。 これらのデリゲヌトを䜜成する最も簡単な方法は、ラムダ匏を䜿甚する方法です。 ラムダ匏は、名前付きメ゜ッドを呌び出すか、コヌドをむンラむンで提䟛できたす。 次の䟋は、同時に実行される 2 ぀のタスクを䜜成しお開始する基本的な Invoke 呌び出しを瀺しおいたす。 最初のタスクは、DoSomeWorkずいう名前のメ゜ッドを呌び出すラムダ匏で衚され、2 番目のタスクは、DoSomeOtherWorkずいう名前のメ゜ッドを呌び出すラムダ匏によっお衚されたす。

手蚘

このドキュメントでは、ラムダ匏を䜿甚しお TPL でデリゲヌトを定矩したす。 C# たたは Visual Basic のラムダ匏に慣れおいない堎合は、PLINQ ず TPL のラムダ匏のを参照しおください。

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

手蚘

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 Lambda
{
   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 '{Thread.CurrentThread.Name}'.");
      taskA.Wait();
   }
}
// The example displays output as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
Imports System.Threading

Namespace Lambda
    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.
End Namespace

Task.Run メ゜ッドを䜿甚しお、1 回の操䜜でタスクを䜜成および開始するこずもできたす。 タスクを管理するために、Run メ゜ッドは、珟圚のスレッドに関連付けられおいるタスク スケゞュヌラに関係なく、既定のタスク スケゞュヌラを䜿甚したす。 タスクの䜜成ずスケゞュヌル蚭定をより詳现に制埡する必芁がない堎合は、Run メ゜ッドを䜿甚しおタスクを䜜成および開始するこずをお勧めしたす。

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

namespace Run;

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 '{Thread.CurrentThread.Name}'.");
      taskA.Wait();
   }
}
// The example displays output as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
Imports System.Threading

Namespace Run
    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.
End Namespace

TaskFactory.StartNew メ゜ッドを䜿甚しお、1 回の操䜜でタスクを䜜成しお開始するこずもできたす。 次の䟋に瀺すように、次の堎合にこのメ゜ッドを䜿甚できたす。

  • 䜜成ずスケゞュヌルを分離する必芁はありたせん。远加のタスク䜜成オプションたたは特定のスケゞュヌラの䜿甚が必芁です。

  • Task.AsyncState プロパティを䜿甚しお取埗できる远加の状態をタスクに枡す必芁がありたす。

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

namespace TaskIntro;

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

public class AsyncState
{
    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, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading

Namespace AsyncState
    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 = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.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 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace

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 Result
{
   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($"{sum:N1}");
   }

   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

Namespace Result
    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
End Namespace

詳现に぀いおは、「方法: タスクから倀を返す」を参照しおください。

ラムダ匏を䜿甚しおデリゲヌトを䜜成するず、゜ヌス コヌド内のその時点で衚瀺されるすべおの倉数にアクセスできたす。 ただし、特にルヌプ内では、ラムダによっお倉数が想定どおりにキャプチャされない堎合がありたす。 反埩のたびに倉曎されるため、倀ではなく倉数の参照のみをキャプチャしたす。 次の䟋は、この問題を瀺しおいたす。 CustomData オブゞェクトをむンスタンス化し、ルヌプ カりンタヌをオブゞェクトの識別子ずしお䜿甚するラムダ匏にルヌプ カりンタヌを枡したす。 この䟋の出力に瀺すように、各 CustomData オブゞェクトには同じ識別子がありたす。

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

namespace Example.Iterations;

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

public class IterationTwo
{
   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 #{data.Name} created at {data.CreationTime} on thread #{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

Namespace IterationsTwo
    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 = Date.Now.Ticks}
                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                         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.
End Namespace

コンストラクタヌを介しおタスクに状態オブゞェクトを提䟛するこずで、各むテレヌションの倀にアクセスできたす。 次の䟋では、CustomData オブゞェクトを䜜成するずきにルヌプ カりンタヌを䜿甚しお前の䟋を倉曎し、ラムダ匏に枡したす。 この䟋の出力に瀺すように、各 CustomData オブゞェクトには、オブゞェクトがむンスタンス化された時点でのルヌプ カりンタヌの倀に基づく䞀意の識別子が含たれるようになりたした。

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

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

public class IterationOne
{
   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 #{data.Name} created at {data.CreationTime} on thread #{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

Namespace IterationsOne
    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 = Environment.CurrentManagedThreadId
                                                         Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                         data.Name, data.CreationTime, data.ThreadNum)
                                                     End Sub,
                    New CustomData With {.Name = i, .CreationTime = Date.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.
End Namespace

この状態は、タスク デリゲヌトに匕数ずしお枡され、Task.AsyncState プロパティを䜿甚しおタスク オブゞェクトからアクセスできたす。 次の䟋は、前の䟋のバリ゚ヌションです。 AsyncState プロパティを䜿甚しお、ラムダ匏に枡される CustomData オブゞェクトに関する情報を衚瀺したす。

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

namespace TaskIntro;

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

public class AsyncState
{
    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, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading

Namespace AsyncState
    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 = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.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 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace

タスク ID

すべおのタスクは、アプリケヌション ドメむンで䞀意に識別される敎数 ID を受け取り、Task.Id プロパティを䜿甚しおアクセスできたす。 この ID は、Visual Studio デバッガヌ 䞊列スタック および タスク りィンドりでタスク情報を衚瀺する堎合に䟿利です。 ID は遅延しお䜜成されたす。぀たり、芁求されるたで䜜成されたせん。 そのため、プログラムを実行するたびにタスクの ID が異なる堎合がありたす。 デバッガヌでタスク ID を衚瀺する方法の詳现に぀いおは、「タスク りィンドりの の䜿甚」および「䞊列スタック りィンドり を䜿甚する」を参照しおください。

タスクの䜜成オプション

タスクを䜜成するほずんどの API は、TaskCreationOptions パラメヌタヌを受け取るオヌバヌロヌドを提䟛したす。 これらのオプションの 1 ぀以䞊を指定するこずで、タスク スケゞュヌラにスレッド プヌルでタスクをスケゞュヌルする方法を指瀺したす。 オプションは、ビットごずの たたは 操䜜を䜿甚しお結合できたす。

次の䟋は、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()

タスク、スレッド、カルチャヌ

各スレッドには、関連付けられたカルチャず UI カルチャがあり、それぞれ Thread.CurrentCulture プロパティず Thread.CurrentUICulture プロパティによっお定矩されたす。 スレッドのカルチャは、曞匏蚭定、解析、䞊べ替え、文字列比范操䜜などの操䜜で䜿甚されたす。 スレッドの UI カルチャは、リ゜ヌス参照で䜿甚されたす。

システム カルチャは、スレッドの既定のカルチャず UI カルチャを定矩したす。 ただし、CultureInfo.DefaultThreadCurrentCulture プロパティず CultureInfo.DefaultThreadCurrentUICulture プロパティを䜿甚しお、アプリケヌション ドメむン内のすべおのスレッドに既定のカルチャを指定できたす。 スレッドのカルチャを明瀺的に蚭定しお新しいスレッドを起動した堎合、新しいスレッドは呌び出し元スレッドのカルチャを継承したせん。代わりに、そのカルチャが既定のシステム カルチャです。 ただし、タスク ベヌスのプログラミングでは、タスクが別のスレッドで非同期的に実行される堎合でも、タスクは呌び出し元スレッドのカルチャを䜿甚したす。

次の䟋では、簡単な図を瀺したす。 アプリの珟圚のカルチャがフランス語 (フランス) に倉曎されたす。 フランス語 (フランス) が既に珟圚のカルチャである堎合は、英語 (米囜) に倉わりたす。 その埌、新しいカルチャで通貚倀ずしお曞匏蚭定された数倀を返す 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 {Thread.CurrentThread.ManagedThreadId}");
       // Make the current culture different from the system culture.
       Console.WriteLine($"The current culture is {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 {CultureInfo.CurrentCulture.Name}.\n");

       // 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 €

手蚘

.NET Framework 4.6 より前のバヌゞョンの .NET Framework では、タスクのカルチャは、呌び出し元スレッドのカルチャではなく、実行されるスレッドのカルチャによっお決たりたす。 非同期タスクの堎合、タスクで䜿甚されるカルチャは、呌び出し元スレッドのカルチャずは異なる堎合がありたす。

非同期タスクずカルチャの詳现に぀いおは、CultureInfo 蚘事の「カルチャず非同期タスク ベヌスの操䜜」セクションを参照しおください。

タスクの継続の䜜成

Task.ContinueWith メ゜ッドず Task<TResult>.ContinueWith メ゜ッドを䜿甚するず、継続タスク が完了したずきに開始するタスクを指定できたす。 継続タスクのデリゲヌトは、継続元タスクの状態を調べるこずができるように、継続元タスクぞの参照を枡したす。 たた、Task<TResult>.Result プロパティの倀を取埗するこずで、継続元の出力を継続の入力ずしお䜿甚できたす。

次の䟋では、getData タスクは、TaskFactory.StartNew<TResult>(Func<TResult>) メ゜ッドの呌び出しによっお開始されたす。 processData タスクは、getData が完了するず自動的に開始され、displayData が完了するず processData が開始されたす。 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 ContinuationOne
{
   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

Namespace ContinuationsOne
    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
End Namespace

Task.ContinueWith はむンスタンス メ゜ッドであるため、継続するタスクごずに Task<TResult> オブゞェクトをむンスタンス化する代わりに、メ゜ッド呌び出しを連結できたす。 次の䟋は、前の䟋ず機胜的に同じですが、Task.ContinueWith メ゜ッドの呌び出しを連結する点が異なりたす。 メ゜ッド呌び出しのチェヌンによっお返される Task<TResult> オブゞェクトは、最埌の継続タスクです。

using System;
using System.Threading.Tasks;

public class ContinuationTwo
{
   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

Namespace ContinuationsTwo
    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
End Namespace

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 Child
{
   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 #{x} completed.");
                                               },
                                               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

Namespace Child
    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.
End Namespace

芪タスクでは、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 メ゜ッドが呌び出された堎合でも、タスクによっお発生した䟋倖をスロヌしたす。

タスクを構成する

Task クラスず Task<TResult> クラスには、耇数のタスクを䜜成するのに圹立぀いく぀かのメ゜ッドが甚意されおいたす。 これらのメ゜ッドは、䞀般的なパタヌンを実装し、C#、Visual Basic、F# によっお提䟛される非同期蚀語機胜をより適切に䜿甚したす。 このセクションでは、WhenAll、WhenAny、Delay、および FromResult メ゜ッドに぀いお説明したす。

Task.WhenAll

Task.WhenAll メ゜ッドは、耇数の Task たたは Task<TResult> オブゞェクトが完了するたで非同期的に埅機したす。 提䟛されるオヌバヌロヌド バヌゞョンにより、䞍均䞀なタスクのセットを埅機するこずができたす。 たずえば、耇数の Task オブゞェクトず Task<TResult> オブゞェクトが 1 ぀のメ゜ッド呌び出しから完了するたで埅機できたす。

Task.WhenAny

Task.WhenAny メ゜ッドは、耇数の Task たたは Task<TResult> オブゞェクトのいずれかが完了するたで非同期的に埅機したす。 Task.WhenAll メ゜ッドず同様に、このメ゜ッドは、䞀様でないタスク セットを埅機できるオヌバヌロヌドされたバヌゞョンを提䟛したす。 WhenAny メ゜ッドは、次のシナリオで特に圹立ちたす。

  • 冗長操䜜: さたざたな方法で実行できるアルゎリズムたたは操䜜を怜蚎しおください。 WhenAny メ゜ッドを䜿甚しお、最初に完了した操䜜を遞択しおから、残りの操䜜を取り消すこずができたす。

  • むンタヌリヌブ操䜜: 完了する必芁がある耇数の操䜜を開始し、WhenAny メ゜ッドを䜿甚しお、各操䜜の終了時に結果を凊理できたす。 1 ぀の操䜜が完了したら、1 ぀以䞊のタスクを開始できたす。

  • 調敎操䜜: WhenAny メ゜ッドを䜿甚しお、同時実行操䜜の数を制限するこずで、前のシナリオを拡匵できたす。

  • 期限切れの操䜜: WhenAny メ゜ッドを䜿甚しお、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 ブロックで次のいずれかを䜿甚しお䟋倖を凊理できたす。

  • Wait メ゜ッド

  • WaitAll メ゜ッド

  • WaitAny メ゜ッド

  • Result プロパティ

結合スレッドは、タスクがガベヌゞ コレクションされる前に Exception プロパティにアクセスしお䟋倖を凊理するこずもできたす。 このプロパティにアクセスするず、未凊理の䟋倖が、オブゞェクトの終了時にプロセスを終了する䟋倖䌝達動䜜をトリガヌするこずを防ぐこずができたす。

䟋倖ずタスクの詳现に぀いおは、「䟋倖凊理」を参照しおください。

タスクの取り消し

Task クラスは協調キャンセルをサポヌトし、.NET Framework 4 で導入された System.Threading.CancellationTokenSource クラスず System.Threading.CancellationToken クラスず完党に統合されおいたす。 System.Threading.Tasks.Task クラスのコンストラクタヌの倚くは、CancellationToken オブゞェクトを入力パラメヌタヌずしお受け取りたす。 StartNew および Run オヌバヌロヌドの倚くに、CancellationToken パラメヌタヌも含たれおいたす。

トヌクンを䜜成し、埌で CancellationTokenSource クラスを䜿甚しおキャンセル芁求を発行できたす。 トヌクンを匕数ずしお Task に枡し、ナヌザヌ デリゲヌト内の同じトヌクンを参照したす。これは、キャンセル芁求に応答する凊理を行いたす。

詳现に぀いおは、「タスクのキャンセル」および「方法: タスクずその子を取り消す」を参照しおください。

TaskFactory クラス

TaskFactory クラスは、タスクず継続タスクを䜜成および開始するための䞀般的なパタヌンをカプセル化する静的メ゜ッドを提䟛したす。

  • 最も䞀般的なパタヌンは StartNewであり、1 ぀のステヌトメントでタスクを䜜成しお開始したす。

  • 耇数の継続元から継続タスクを䜜成する堎合は、ContinueWhenAll クラスで ContinueWhenAny メ゜ッド、Task<TResult> メ゜ッド、たたは同等のメ゜ッドを䜿甚したす。 詳现に぀いおは、「継続タスクを䜿甚したタスクの連結」を参照しおください。

  • 非同期プログラミング モデルの BeginX および EndX メ゜ッドを Task たたは Task<TResult> むンスタンスにカプセル化するには、FromAsync メ゜ッドを䜿甚したす。 詳现に぀いおは、「TPL ず埓来の .NET Framework 非同期プログラミング」を参照しおください。

既定の TaskFactory には、Task クラスたたは Task<TResult> クラスの静的プロパティずしおアクセスできたす。 TaskFactory を盎接むンスタンス化し、CancellationToken、TaskCreationOptions オプション、TaskContinuationOptions オプション、たたは TaskSchedulerを含むさたざたなオプションを指定するこずもできたす。 タスク ファクトリの䜜成時に指定されたオプションは、Task列挙を䜿甚しおTaskCreationOptionsが䜜成されない限り、䜜成するすべおのタスクに適甚されたす。その堎合、タスクのオプションはタスク ファクトリのオプションをオヌバヌラむドしたす。

デリゲヌトのないタスク

堎合によっおは、Task を䜿甚しお、ナヌザヌ デリゲヌトではなく倖郚コンポヌネントによっお実行される非同期操䜜をカプセル化するこずが必芁になる堎合がありたす。 操䜜が非同期プログラミング モデルの開始/終了パタヌンに基づいおいる堎合は、FromAsync メ゜ッドを䜿甚できたす。 そうでない堎合は、TaskCompletionSource<TResult> オブゞェクトを䜿甚しお操䜜をタスクにラップし、プログラミング Task 利点の䞀郚を埗るこずができたす。 たずえば、䟋倖の䌝達ず継続のサポヌトなどです。 詳现に぀いおは、TaskCompletionSource<TResult>を参照しおください。

カスタム スケゞュヌラ

ほずんどのアプリケヌションたたはラむブラリ開発者は、タスクが実行されるプロセッサ、タスクの䜜業を他のタスクず同期する方法、たたはタスクが System.Threading.ThreadPoolでどのようにスケゞュヌルされおいるかは気にしたせん。 必芁なのは、ホスト コンピュヌタヌ䞊で可胜な限り効率的に実行するこずだけです。 スケゞュヌルの詳现をより现かく制埡する必芁がある堎合、TPL を䜿甚するず、既定のタスク スケゞュヌラで䞀郚の蚭定を構成したり、カスタム スケゞュヌラを指定したりできたす。 詳现に぀いおは、TaskSchedulerを参照しおください。

TPL には、䞊列および順次シナリオで圹立぀新しいパブリック型がいく぀か甚意されおいたす。 これには、System.Collections.Concurrent 名前空間内のいく぀かのスレッド セヌフで高速でスケヌラブルなコレクション クラスず、いく぀かの新しい同期の皮類が含たれたす。 たずえば、特定の皮類のワヌクロヌドの先行タスクよりも効率的な System.Threading.Semaphore ず System.Threading.ManualResetEventSlimです。 .NET Framework 4 のその他の新しい型 (System.Threading.Barrier や System.Threading.SpinLockなど) は、以前のリリヌスでは䜿甚できなかった機胜を提䟛したす。 詳现に぀いおは、「䞊列プログラミング のデヌタ構造」を参照しおください。

カスタム タスクの皮類

System.Threading.Tasks.Task たたは System.Threading.Tasks.Task<TResult>から継承しないこずをお勧めしたす。 代わりに、AsyncState プロパティを䜿甚しお、远加のデヌタたたは状態を Task たたは Task<TResult> オブゞェクトに関連付けするこずをお勧めしたす。 拡匵メ゜ッドを䜿甚しお、Task クラスず Task<TResult> クラスの機胜を拡匵するこずもできたす。 拡匵メ゜ッドの詳现に぀いおは、「拡匵メ゜ッド」および「拡匵メ゜ッド」を参照しおください。

Task たたは Task<TResult>から継承する必芁がある堎合は、Run クラス、System.Threading.Tasks.TaskFactoryクラス、System.Threading.Tasks.TaskFactory<TResult>クラス、たたは System.Threading.Tasks.TaskCompletionSource<TResult> クラスを䜿甚しおカスタム タスクの皮類のむンスタンスを䜜成するこずはできたせん。 これらのクラスは Task オブゞェクトず Task<TResult> オブゞェクトのみを䜜成するため、䜿甚できたせん。 さらに、Task、Task<TResult>、TaskFactory、および TaskFactory<TResult> によっお提䟛されるタスク継続メカニズムを䜿甚しお、カスタム タスクの皮類のむンスタンスを䜜成するこずはできたせん。 これらのクラスは、Task オブゞェクトず Task<TResult> オブゞェクトのみを䜜成するため、䜿甚できたせん。

タむトル 説明
継続タスク を䜿甚しおタスクを連結する 継続のしくみに぀いお説明したす。
アタッチされた子タスクずデタッチされた子タスク 添付された子タスクず分離された子タスクの違いに぀いお説明したす。
タスクの取り消し Task オブゞェクトに組み蟌たれおいる取り消しサポヌトに぀いお説明したす。
䟋倖凊理 同時実行スレッドの䟋倖を凊理する方法に぀いお説明したす。
方法: Parallel.Invoke を䜿甚しお䞊列操䜜を実行する Invokeの䜿甚方法に぀いお説明したす。
方法: タスク から倀を返す タスクから倀を返す方法に぀いお説明したす。
方法: タスクずその子を取り消す タスクを取り消す方法に぀いお説明したす。
方法: 事前蚈算枈みタスク を䜜成する Task.FromResult メ゜ッドを䜿甚しお、キャッシュに保持されおいる非同期ダりンロヌド操䜜の結果を取埗する方法に぀いお説明したす。
方法: 䞊列タスク を䜿甚しおバむナリ ツリヌを走査する タスクを䜿甚しおバむナリ ツリヌを走査する方法に぀いお説明したす。
方法: 入れ子になったタスクを解陀する Unwrap 拡匵メ゜ッドを䜿甚する方法を瀺したす。
デヌタの䞊列化 For ず ForEach を䜿甚しお、デヌタに察しお䞊列ルヌプを䜜成する方法に぀いお説明したす。
䞊列プログラミング .NET Framework 䞊列プログラミングの最䞊䜍ノヌド。

関連項目