非同期処理の例え話
朝食を作って食べる時を例に考えましょう
朝食の用意
ここでは、身近な例として朝食を作るときの料理手順を考えてみましょう
順番
- フライパンを熱し、卵を焼く
 - コーヒーを淹れる
 - 皿を並べる
 
イメージ
前の処理が終わると次の処理といったような継続処理を指します

同期処理(順次実行)では、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つの処理同時としています
終わる時間は分けていますので、順番に終了し、最後まで終わると「作り終わり」と表示されるのがわかりますね
まとめ
非同期処理はどのような時に必要でしょう
- ネットワークから大きなデータをダウンロード中に他の処理をしたい場合
 - 複数のセンサーからの情報を同時に取得したい場合(各センサーからの受信に時間がかかるケース)
 
などがありますね






ディスカッション
コメント一覧
まだ、コメントがありません