はじめに
この記事は、@acple@github さんによって書かれた記事のまとめというか焼き直しになります。
要は下記の記事に出てきたプログラムを全部まとめて実行するコードを書いた(そしたら見やすかった→記事にした)というだけの話で、詳細な解説は書きません。
ぜひとも下記リンク先の記事を読んで、なんとなく雰囲気をつかんでから実行してみてださい。
https://qiita.com/acple@github/items/8f63aacb13de9954c5da
コード
(C# .NETFramework,Version=v4.5.2 コンソールアプリケーションで動作確認済)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TaskAndThread
{
class Program
{
static void Main(string[] args)
{
Parts p = new Parts();
p.RunHeavyMethodSync();
p.RunHeavyMethodAsync1(); // 警告出るけどシカトでok
p.RunHeavyMethodAsync2();
p.RunHeavyMethodParallel1();
p.RunHeavyMethodParallel2(); // waitなし
//p.RunHeavyMethodParallel2().Wait(); // waitあり 両者で比較してみてください。
p.KusoMethod();
Console.WriteLine("Press any key to quit. (うそです) "); // 先に出る
System.Threading.Thread.Sleep(8000); // Mainスレッドにsleepをかけている
Console.WriteLine("Press any key to quit. (本当だよ) "); // ↑のsleepをコメントにすると、当然この行も先に出る
Console.Read();
}
}
class Parts
{
// 現実のプログラミングではここが重い処理になる。今回はsleepで代用
private void HeavyMethod(int x, string refMethod)
{
Thread.Sleep(5 * (100 - x)); // てきとーに時間を潰す
Console.WriteLine(refMethod + " : " + x);
}
// 比較のため、ただの同期メソッド
public void RunHeavyMethodSync()
{
for (var i = 0; i < 10; i++)
{
var x = i;
HeavyMethod(x, "HM ");
}
}
// -------------------------------------
// 「HeavyMethodを実行する」というタスクを開始し、完了するまで待機を
// 10回繰り返すというタスクを表すので、これは順次動作であり、並列ではない。
public async Task RunHeavyMethodAsync1()
{
for (var i = 0; i < 10; i++)
{
var x = i;
await Task.Run(() => HeavyMethod(x, "HM1"));
}
}
// -------------------------------------
// 動作はRunHeavyMethodAsync1と同じだけど、
// HeavyMethodの実行がいつ完了するのか知ることができない。つらい。
//
public async void RunHeavyMethodAsync2() // RunHeavyMethodAsync1の戻り値がvoidになっただけ
{
for (var i = 0; i < 10; i++)
{
var x = i;
await Task.Run(() => HeavyMethod(x, "HM2"));
}
}
// -------------------------------------
// HeavyMethodを開始せよという命令を、10回繰り返すだけなので、
// これは並列動作になる。Task.Runが投げっぱなしなので、HeavyMethodの状態がわからなくてつらい。
//
public void RunHeavyMethodParallel1() // asyncじゃない
{
for (var i = 0; i < 10; i++)
{
var x = i;
Task.Run(() => HeavyMethod(x, "HP1"));
}
}
// -------------------------------------
// 全てのTaskが完了した時に完了扱いになるたった一つのTaskを作成
// 非同期メソッドではないが、戻り値がTaskなのでこのメソッドは一つのタスクを表しているといえる。
//
public Task RunHeavyMethodParallel2() // asyncじゃないけど、戻り値がTask
{
var tasks = new List<Task>(); // TaskをまとめるListを作成
for (var i = 0; i < 10; i++)
{
var x = i;
var task = Task.Run(() => HeavyMethod(x, "HP2")); // HeavyMethodを開始するというTask
tasks.Add(task); // を、Listにまとめる
}
return Task.WhenAll(tasks);
}
// -------------------------------------
// デッドロックを回避するサンプル ------
public void KusoMethod()
{
NiceAsyncMethod().Wait(); // Waitしてもデッドロックしない!
Console.WriteLine("I am KusoMethod()");
}
private async Task NiceAsyncMethod()
{
await Task.Delay(1000).ConfigureAwait(false); // ←重要
}
// -------------------------------------
}
}
実行結果
HM : 0
HM : 1
HM : 2
HM : 3
HM : 4
HM : 5
HM : 6
HM : 7
HM : 8
HM : 9
HP1 : 1
HM1 : 0
HM2 : 0
HP1 : 0
HP1 : 3
HP1 : 2
HM2 : 1
HM1 : 1
HP1 : 5
I am KusoMethod()
Press any key to quit. (うそです)
HP1 : 4
HM2 : 2
HM1 : 2
HP1 : 7
HP1 : 6
HM2 : 3
HM1 : 3
HP1 : 9
HP1 : 8
HM2 : 4
HM1 : 4
HP2 : 1
HP2 : 0
HM2 : 5
HM1 : 5
HP2 : 4
HP2 : 3
HP2 : 2
HM1 : 6
HM2 : 6
HP2 : 7
HP2 : 6
HP2 : 5
HM1 : 7
HM2 : 7
HP2 : 9
HP2 : 8
HM2 : 8
HM1 : 8
HM2 : 9
HM1 : 9
Press any key to quit. (本当だよ)