こんにちは。
本日はTaskを使用した非同期処理についてのメモです。
概要
Taskは.NET Framework 4で導入された
非同期処理を実現するためのクラスです。
非同期処理Threadに関しては、
以下の記事を参考にしてください。
【C#】Threadを使用した非同期処理 - なんだかGoodVibes
スレッドの開始と待機
以下のサンプルでは、重い処理を定義して
その処理を非同期で実施しています。
Action<object> action = (object obj) => { int max; try { max = (int) obj; } catch (Exception) { max = 10; } Console.WriteLine($"max = {max}"); for (var i = 0; i < max; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] i = {i}"); } }; var t = new Task(action, 5); t.Start(); t.Wait(); Console.WriteLine("Completed!");
以下の処理では、Taskを生成しています。
この時点ではあくまでも生成のみで、開始はしていないので注意してください。
var t = new Task(action, 5);
以下の処理で非同期処理を開始します。
t.Start();
以下の処理で非同期処理を待機します。
t.Wait();
実行結果は以下となります。
max = 5 [13:26:57] i = 0 [13:26:58] i = 1 [13:26:59] i = 2 [13:27:00] i = 3 [13:27:01] i = 4 Completed!
スレッドの開始と待機(Task.Runを使用)
以下のサンプルでは、 .NET Framework 4.5にて導入された
Run
を使用して非同期処理を実施します。
var t = Task.Run(() => { Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Start"); Thread.Sleep(TimeSpan.FromSeconds(3)); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] End"); }); t.Wait(); Console.WriteLine("Completed!");
以下の処理で非同期処理を開始します。
var t = Task.Run(() => { Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Start"); Thread.Sleep(TimeSpan.FromSeconds(3)); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] End"); });
以下の処理で非同期処理を待機します。
t.Wait();
実行結果は以下です。
[13:32:35] Start [13:32:38] End Completed!
複数のタスクの待機
以下の重い処理を複数のタスクで実行した場合のサンプルです。
public void HeavyProc(object obj) { int max = (int) obj; for (var i = 0; i < max; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][{Task.CurrentId}] i = {i}"); } }
複数のタスクのうちどれか一つが終了するのを待機する
指定したタスクのうち、どれか1つでも終了したら
待機を終了するには、WaitAny
を使用します。
var t1 = Task.Run(() => HeavyProc(3)); var t2 = Task.Run(() => HeavyProc(2)); var t3 = Task.Run(() => HeavyProc(1)); Task.WaitAny(new []{t1, t2, t3}); Console.WriteLine("Completed!");
実行結果は以下です。
[13:45:37][1] i = 0 [13:45:37][3] i = 0 [13:45:37][2] i = 0 Completed!
最小のループ回数が1回なので、
1回を指定したタスクが終了すると待機が完了して
処理が進んでいるのがわかります。
すべてのタスクが終了するのを待機する
指定したタスクのうち、すべてが終了してから
待機を終了するには、WaitAll
を使用します。
var t1 = Task.Run(() => HeavyProc(3)); var t2 = Task.Run(() => HeavyProc(2)); var t3 = Task.Run(() => HeavyProc(1)); Task.WaitAll(new []{t1, t2, t3}); Console.WriteLine("Completed!");
実行結果は以下です。
[13:45:44][5] i = 0 [13:45:44][6] i = 0 [13:45:44][4] i = 0 [13:45:45][6] i = 1 [13:45:45][4] i = 1 [13:45:46][4] i = 2 Completed!
最大のループ回数が3回なので、
3回を指定したタスクが終了すると待機が完了して
処理が進んでいるのがわかります。
つまり、すべてのタスクが完了するのを待機しています。
async/await で待機
タスクの待機を Wait
を使用せず、
await
を使用して待機しています。
await
を使用する場合、メソッドは
async
を付与- 戻りの型は
Task
もしくはTask<戻り値の型>
とする必要があります。
今回、戻り値はないので、戻りの型はTask
としています。
public async Task Proc() { var t = Task.Run(() => HeavyProc(2)); await t; Console.WriteLine("Completed!"); }
実行結果は以下です。
[17:17:40][1] i = 0 [17:17:41][1] i = 1 Completed!
以上です。