Task.Factory.StartNewメソッドとTask.Runメソッドについて

Task.Factory.StartNewメソッドまたはTask.Runメソッドを使用してタスクを作成する場合、非同期コードを作成する際には、特定の重要な点に留意する必要があります。ほとんどの場合、非同期コードを使用している場合は、Task.Factory.StartNewメソッドの使用を避けることをお勧めします。並列コードを使用している場合は、StartNewが適切な選択だと思います。

タスクスケジューラは、タスクのスケジューリングを担当するコンポーネントです。.Net Frameworkは、2つのタスクスケジューラを提供します。.Net Frameworkスレッドプールで実行されるデフォルトのタスクスケジューラがあり、指定されたターゲットの同期コンテキストで実行されるタスクスケジューラがあります。ほとんどの場合、デフォルトのタスクスケジューラで十分ですが、独自のカスタムタスクスケジューラを構築して、追加の機能を提供することもできます。独自のカスタムタスクスケジューラを構築するには、System.Threading.Tasks.TaskSchedulerクラスを拡張するクラスを作成する必要があります。

タスク並列ライブラリを使用してタスクを作成するにはどうすればよいですか?

.Netでタスクを作成および開始する方法はいくつかあります。タスク(スケジュール可能な作業単位)を作成するには、System.Threading.Tasks.TaskまたはSystem.Threading.Tasks.Taskクラスを使用する必要があります。前者は値を返さないタスクを作成するために使用されますが、後者は戻り値を持つタスクを作成するために使用されます。Task.Factoryプロパティは、TaskFactoryクラスのインスタンスです。このプロパティは、タスクの作成とスケジュールに使用されます。Task.Factory.StartNewメソッドはフォーク操作のように機能し、新しいタスクを作成および開始するために使用されますが、Waitメソッドは結合操作のように機能し、タスクが完了するのを待ちます。

次のコードスニペットは、Task.Factory.StartNewメソッドの使用方法を示しています。

Task.Factory.StartNew(() => TestMethod(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);

以下のコードスニペットに示すように、Task.Runメソッドを使用してタスクを作成することもできます。

public async Task DoSomeWork()

        {

            await Task.Run(() => TestMethod());

        }

        void TestMethod()

        {

            Console.WriteLine("Hello world!");

        }

タスクから値を返したい場合は、以下のコードスニペットに示すようにTask.FromResultメソッドを利用できます。

public async Task DoSomeWork()

   {

      string text = await Task.FromResult(GetMessage());

   }

private string GetMessage()

   {

      return "Hello world!";

   }

デリゲートまたはアクションを使用してタスクを作成することもできます。次のコードスニペットは、アクションとデリゲートを使用してタスクを作成する方法を示しています。

Task task1 = new Task (new Action(Display));

task1.Start();

Task task2 = new Task (delegate { Display(); });

task2.Start();

lambaおよびanonymousメソッドを使用してタスクを作成することもできます。

Task.Factory.StartNewおよびTask.Run

Task.Factory.StartNewは、タスクを作成して開始する簡単な方法です。Task.Factory.StartNewの呼び出しは、タスクインスタンスを作成してから、インスタンスでStartメソッドを呼び出すことと機能的に同等であることに注意してください。ただし、十分な理由から使用することはお勧めしません。同期コードを実行したい場合は、Task.Factory.StartNewは適切な選択ではありません。

タスクスケジューラが使用可能な場合、StartNewメソッドはそのタスクスケジューラでタスクを実行することに注意してください。逆に、スケジューラーが使用できない場合は、スレッドプールスレッドでタスクを実行します。Task.Factory.StartNewのデフォルトはTaskScheduler.CurrentではなくTaskScheduler.Currentであることに注意してください。

Task.Run(action)の呼び出しは、次のステートメントと同等であることに注意してください。 Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

それどころか、Task.Factory.StartNew(action)の呼び出しは、次のステートメントと同等です。

Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);

カスタムタスクスケジューラを作成し、スケジューラインスタンスを明示的に渡す場合は、Task.Factory.StartNewを使用することをお勧めします。 Task.Runははるかに単純で、デフォルトがより安全であるため、常に使用することをお勧めします。つまり、タスクスケジューラを作成し、StartNewメソッドを呼び出して新しいタスクを作成してスケジュールするときに明示的に渡す必要がない限り、Task.Factory.StartNewの使用を避ける必要があります。 TaskFactory.StartNewメソッドを効果的かつ確実に使用する場合は、カスタムタスクスケジューラを使用してから、CancellationTokenとTaskCreationOptionsを指定する必要があります。

Task.Runメソッドは、スレッドのスケジューリングとその複雑さを細かく制御する必要がない場合に使用することをお勧めします。Task.Runは、主にCPUバウンドメソッドで使用する必要があります。ただし、タスクの実装内ではなく、タスクの呼び出し中にTask.Runを使用する必要があります。つまり、Task.Runは、メソッドの実装内ではなく、メソッドが呼び出された時点で使用する必要があります。例として、次のコードスニペットは「悪い」コードの例です。

public async Task DownloadDataFromWebAsync(Uri uri)

        {

            return await Task.Run(() =>

            {

                using (WebClient webClient = new WebClient())

                {

                    return webClient.DownloadString(uri);

                }

            });

        }

上記のコードスニペットを参照してください。このメソッドは、バックグラウンドスレッドをブロックし、スレッドプールからスレッドを取得して、そのスレッドで同期的に実行するため、スケーラブルではありません。したがって、システムでより多くのリソースを消費します。