非同期処理の例え話
朝食を作って食べる時を例に考えましょう
朝食の用意
ここでは、身近な例として朝食を作るときの料理手順を考えてみましょう
順番
- フライパンを熱し、卵を焼く
- コーヒーを淹れる
- 皿を並べる
イメージ
前の処理が終わると次の処理といったような継続処理を指します
同期処理(順次実行)では、1が終わったら2、2が終わったら3・・・と処理されます
卵を焼いている間、お皿は並べないのです
コード
まずは順番に実行する様子をコードにしてみましょう
この手順の方法を同期処理と呼びます
一番簡単なコードは次のようになります
Console.WriteLine("卵を焼く");
Console.WriteLine("コーヒーを淹れる");
Console.WriteLine("皿を並べる");
行は増えますが、クラス、メソッドに分解して構成しなおしてみましょう
Egg egg = new Egg();
egg.Fly();
Coffee coffee = new Coffee();
coffee.Pour();
Dish dish = new Dish();
dish.Arrange();
class Egg
{
public void Fly()
{
Console.WriteLine("卵を焼く");
Thread.Sleep(3000);
}
}
class Coffee
{
public void Pour()
{
Console.WriteLine("コーヒーを淹れる");
Thread.Sleep(2000);
}
}
class Dish
{
public void Arrange()
{
Console.WriteLine("皿を並べる");
Thread.Sleep(500);
}
}
結果
実行結果を見てみましょう
イメージの通り、順番に実行されているのがわかります
朝食を並行して準備する
イメージ
準備を同時に実行するイメージです
卵を焼きながら、コーヒーを淹れ、お皿も用意します
実際はこのようにしますよね
一つが終わるまで、じっと待ったりしません
同期コードを非同期に変更して、確認してみましょう
まず、上記のコードを少し変更しておきます
朝食クラスにカスタマイズ
朝食クラスを作り、必要なコードをメソッド化します
Breakfast breakfast = new Breakfast();
breakfast.Cook();
class Breakfast
{
public void Cook()
{
Egg egg = FlyEgg();
egg.Fly();
Coffee coffee = PourCoffee();
coffee.Pour();
Dish dish = ArrangeDish();
dish.Arrange();
Egg FlyEgg()
{
return new Egg();
}
Coffee PourCoffee()
{
return new Coffee();
}
Dish ArrangeDish()
{
return new Dish();
}
}
}
class Egg
{
public void Fly()
{
Console.WriteLine("卵を焼く");
Thread.Sleep(3000);
}
}
class Coffee
{
public void Pour()
{
Console.WriteLine("コーヒーを淹れる");
Thread.Sleep(2000);
}
}
class Dish
{
public void Arrange()
{
Console.WriteLine("皿を並べる");
Thread.Sleep(500);
}
}
非同期コードに変更
同時実行できるように変更します
次は、Fly()メソッドの終了を待つコードです
この次の行に処理を移行するには、Fly()メソッドが終わらないといけません
ただし、待つ間は呼び出し元に制御が戻ります
システムがロックすることがないのが特徴です
await egg.Fly();
次は、タスクの終了を待つコードです
タスクの終了を知ることにも使います
この次の行に処理を移すには、タスク一覧が全て終了していなければなりません
Task.WaitAll(タスク一覧)
Breakfast breakfast = new Breakfast();
breakfast.Cook();
class Breakfast
{
public void Cook()
{
Task<Egg> eggTask = FlyEggAsync();
Task<Coffee> coffeeTask = PourCoffeeAsync();
Task<Dish> dishTask = ArrangeDishAsync();
Task.WaitAll(new Task[] { eggTask, coffeeTask, dishTask });
Console.WriteLine("作り終わり");
}
async Task<Egg> FlyEggAsync()
{
var egg = new Egg();
await egg.Fly();
return egg;
}
async Task<Coffee> PourCoffeeAsync()
{
var coffee = new Coffee();
await coffee.Pour();
return coffee;
}
async Task<Dish> ArrangeDishAsync()
{
var dish = new Dish();
await dish.Arrange();
return dish;
}
}
class Egg
{
public async Task<Egg> Fly()
{
Console.WriteLine("卵を焼き始める");
await Task.Delay(3000);
Console.WriteLine("卵を焼き終わり");
return this;
}
}
class Coffee
{
public async Task<Coffee> Pour()
{
Console.WriteLine("コーヒーを淹れ始める");
await Task.Delay(2000);
Console.WriteLine("コーヒーを淹れ終わり");
return this;
}
}
class Dish
{
public async Task<Dish> Arrange()
{
Console.WriteLine("皿を並べ始める");
await Task.Delay(2000);
Console.WriteLine("皿を並べ終わり");
return this;
}
}
結果
実行結果を見てみましょう
わかりやすくするためにスタートは3つの処理同時としています
終わる時間は分けていますので、順番に終了し、最後まで終わると「作り終わり」と表示されるのがわかりますね
まとめ
非同期処理はどのような時に必要でしょう
- ネットワークから大きなデータをダウンロード中に他の処理をしたい場合
- 複数のセンサーからの情報を同時に取得したい場合(各センサーからの受信に時間がかかるケース)
などがありますね
ディスカッション
コメント一覧
まだ、コメントがありません