半人前プログラマーの技術談議

開発したもの紹介していくブログ

C# 非同期処理編 様々なメソッド① part1-3

今回は非同期処理の様々な機能について紹介していきます.

Wait

waitは指定したタスクの処理が終わるまで,待機するメソッドです.

例えばこんな事があります.

サブタスクの処理中に,メインタスクの処理が終わってしまったらどうなってしまうのか?

図にするとこんな感じです.

 

f:id:pokoshirou:20180927195932p:plain

 

 

では,早速検証していきたいと思います.

 

ソースコード 

static void Main(string[] args)
{
    Console.WriteLine("メインタスクの処理開始");

    Console.WriteLine("メイン1");

    var task = SubTask();   // サブタスクを動かす

    Thread.Sleep(1 * 1000); // 1秒待機する
    Console.WriteLine("メイン2");

    Thread.Sleep(1 * 1000);    // 1秒待機する
    Console.WriteLine("メイン3");

    Console.WriteLine("メインタスクの処理完了");
}

static async Task SubTask()
{
    // 別スレッドでタスクを動かす
    var task = Task.Run(() =>
    {
        Console.WriteLine("サブタスクの処理開始");
        Console.WriteLine("サブ1");

        Thread.Sleep(5 * 1000);    // 5秒待機する

        Console.WriteLine("サブ2");
        Console.WriteLine("サブタスクの処理完了");
    });

    await task;
}

 

待機時間に注目しましょう.

サブタスクの待機時間の方が3秒長いです.

 

実行結果

f:id:pokoshirou:20180927200443p:plain

 

サブタスクの処理が最後まで完了していない状態で,メインタスクの処理が完了してしまい,プログラムが終了しています.

 

これを避ける為に,waitを使います.

使い方は,待機したい場所に指定したタスクのwaitメソッドを記述するだけでできます.

メインタスクの処理完了後,待機させる場合,以下のようになります.

 

ソースコード

static void Main(string[] args)
{
    Console.WriteLine("メインタスクの処理開始");

    Console.WriteLine("メイン1");

    var task = SubTask();   // サブタスクを動かす

    Thread.Sleep(1 * 1000); // 1秒待機する
    Console.WriteLine("メイン2");

    Thread.Sleep(1 * 1000);    // 1秒待機する
    Console.WriteLine("メイン3");

    Console.WriteLine("メインタスクの処理完了");

    task.Wait(); // 変更箇所
}

static async Task SubTask()
{
    var task = Task.Run(() =>
    {
        Console.WriteLine("サブタスクの処理開始");
        Console.WriteLine("サブ1");

        Thread.Sleep(5 * 1000);    // 5秒待機する

        Console.WriteLine("サブ2");
        Console.WriteLine("サブタスクの処理完了");
    });

    await task;
}

 

実行結果

f:id:pokoshirou:20181004184638p:plain

 

メインタスクの処理完了後,サブタスクの処理が完了するまで待機しています.

 

 

 

 

 

Result

waitの機能に戻り値を取得するメソッドとして,Resultがあります.

使い方はwaitと同じで,戻り値があるものだと思って貰えればいいです.

 

ソースコード

static void Main(string[] args)
{
    int subsum; // サブタスクからの結果を取得する為の変数

    Console.WriteLine("メインタスクの処理開始");

    var task = SubTask();   // サブタスクを動かす

    // 1から50までの和を求める
    int sum = 0;
    for (int i = 1; i <= 50; i++)
    {
        sum += i;
    }

    Thread.Sleep(3 * 1000); // 3秒待機する

    subsum = task.Result; // サブタスクからの結果を待つ

    Console.WriteLine(sum + " + " + subsum + " = " + (sum + subsum));

    Console.WriteLine("メインタスクの処理完了");
}

static async Task<int> SubTask()
{
    int sum = await Task.Run(() =>
    {
        Console.WriteLine("サブタスクの処理開始");

        // 1から100までの和を求める
        int sum1 = 0;
        for (int i = 1; i <= 100; i++)
        {
            sum1 += i;
        }
                
        Thread.Sleep(5 * 1000);    // 5秒待機する

        Console.WriteLine("サブタスクの処理完了");

        return sum1;
    });

    return sum;
}

少々わかりにくいと思いますが,Taskの戻り値を取得させる為に,サブタスクをTask<int>にしました.

 

実行結果

f:id:pokoshirou:20181004194124p:plain

値を取得してからメインタスクの処理を完了させて,プログラムを終了しています.

 

 

 

 

 

Thread.SleepとTask.Delay

Thread.Sleepについて,特に説明はしていませんが,

part1-1でも使っていたように,同期的にミリ秒単位で待機するメソッドです.

 

それに対してTask.Delayは,非同期的に待機します.

簡単に言ってしまえば,

Task.Run(() =>
{
    Thread.Sleep();
});

これとやってる事と変わりないです.

awaitと組み合わせて使う事が多いかな?

 

 

 

今回はここまで!