非同期処理の例え話(簡易版)
朝食を作って食べる時を例に考えましょう
朝食の用意
ここでは、身近な例として朝食を作るときの料理手順を考えてみましょう
順番
- フライパンを熱し、卵を焼く
- コーヒーを淹れる
- 皿を並べる
イメージ
前の処理が終わると次の処理といったような継続処理を指します
同期処理(順次実行)では、1が終わったら2、2が終わったら3・・・と処理されます
卵を焼いている間、お皿は並べないのです
コード
まずは順番に実行する様子をコードにしてみましょう
この手順の方法を同期処理と呼びます
一番簡単なコードは次のようになります
Console.WriteLine("卵を焼く");
Console.WriteLine("コーヒーを淹れる");
Console.WriteLine("皿を並べる");
結果
実行結果を見てみましょう
イメージの通り、順番に実行されているのがわかります
朝食を並行して準備する
イメージ
準備を同時に実行するイメージです
卵を焼きながら、コーヒーを淹れ、お皿も用意します
実際はこのようにしますよね
一つが終わるまで、じっと待ったりしません
同期コードを非同期に変更して、確認してみましょう
まず、上記のコードを少し変更しておきます
朝食クラスにカスタマイズ
朝食の用意ごとにメソッドを作ります
非同期コードに変更
同時実行できるように変更します
次のコードで3000ms(3秒)待ちます
3秒経過しないと下の行には進めません
ただし、待つ間は制御が呼び出し元に戻ります
システムがロックすることがないのが特徴です
await Task.Delay(3000);
Thread.Sleep(20000);
Console.WriteLine("朝食を母親(妻、etc)にお願い開始");
Console.WriteLine("卵を焼く依頼");
FlyEggAsync();
Console.WriteLine("コーヒーを淹れる依頼");
PourCoffeeAsync();
Console.WriteLine("皿を並べる依頼");
ArrangeDishAsync();
Console.WriteLine("依頼を終えた");
Console.ReadLine();
async void FlyEggAsync()
{
Console.WriteLine("卵を焼き始める");
await Task.Delay(7000);
Console.WriteLine("卵を焼き終わり");
}
async void PourCoffeeAsync()
{
Console.WriteLine("コーヒーを淹れ始める");
await Task.Delay(5000);
Console.WriteLine("コーヒーを淹れ終わり");
}
async void ArrangeDishAsync()
{
Console.WriteLine("皿を並べ始める");
await Task.Delay(2000);
Console.WriteLine("皿を並べ終わり");
}
結果
実行結果を見てみましょう
わかりやすくするためにスタートは3つの処理同時としています
それぞれのメッセージとコードの関係を確認してみましょう
まとめ
非同期処理はどのような時に必要でしょう
- ネットワークから大きなデータをダウンロード中に他の処理をしたい場合
- 複数のセンサーからの情報を同時に取得したい場合(各センサーからの受信に時間がかかるケース)
などがありますね
参考
おまけ
すべての処理が終わった後に処理したいことがある場合
//Thread.Sleep(20000);
Console.WriteLine("朝食を母親(妻、etc)にお願い開始");
Console.WriteLine("卵を焼く依頼");
Task<string> flyEggTask = FlyEggAsync();
Console.WriteLine("コーヒーを淹れる依頼");
Task<string> pourCoffeeTask = PourCoffeeAsync();
Console.WriteLine("皿を並べる依頼");
Task<string> arrangeDishTask = ArrangeDishAsync();
Console.WriteLine("依頼を終えた");
Task.WaitAll(new Task[] { flyEggTask, pourCoffeeTask, arrangeDishTask });
Console.WriteLine(flyEggTask.Result);
Console.WriteLine(pourCoffeeTask.Result);
Console.WriteLine(arrangeDishTask.Result);
Console.WriteLine("僕は朝食をいただいた");
async Task<string> FlyEggAsync()
{
Console.WriteLine("卵を焼き始める");
await Task.Delay(7000);
return "卵を焼き終わり";
}
async Task<string> PourCoffeeAsync()
{
Console.WriteLine("コーヒーを淹れ始める");
await Task.Delay(5000);
return "コーヒーを淹れ終わり";
}
async Task<string> ArrangeDishAsync()
{
Console.WriteLine("皿を並べ始める");
await Task.Delay(2000);
return "皿を並べ終わり";
}
個別の完了を知りたい場合
//Thread.Sleep(20000);
Console.WriteLine("朝食を母親(妻、etc)にお願い開始");
Console.WriteLine("卵を焼く依頼");
Task<string> flyEggTask = FlyEggAsync();
Console.WriteLine("コーヒーを淹れる依頼");
Task<string> pourCoffeeTask = PourCoffeeAsync();
Console.WriteLine("皿を並べる依頼");
Task<string> arrangeDishTask = ArrangeDishAsync();
Console.WriteLine("依頼を終えた");
var breakfastTasks = new List<Task> { flyEggTask, pourCoffeeTask, arrangeDishTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == flyEggTask)
{
Console.WriteLine("卵焼きできましたよ");
}
else if (finishedTask == pourCoffeeTask)
{
Console.WriteLine("コーヒーを淹れましたよ");
}
else if (finishedTask == arrangeDishTask)
{
Console.WriteLine("お皿を並べ終わりましたよ");
}
breakfastTasks.Remove(finishedTask);
}
async Task<string> FlyEggAsync()
{
Console.WriteLine("卵を焼き始める");
await Task.Delay(7000);
return "卵を焼き終わり";
}
async Task<string> PourCoffeeAsync()
{
Console.WriteLine("コーヒーを淹れ始める");
await Task.Delay(5000);
return "コーヒーを淹れ終わり";
}
async Task<string> ArrangeDishAsync()
{
Console.WriteLine("皿を並べ始める");
await Task.Delay(2000);
return "皿を並べ終わり";
}
ディスカッション
コメント一覧
まだ、コメントがありません