質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

C#のasync,awaitについて

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 891

score 17

C#におけるasync,awaitの動作の仕方をあまりイメージできていないでいます。

聞きたい点

  • 非同期処理の流れ

質問

https://tech-lab.sios.jp/archives/15711
async,awaitを理解するために上記のサイトを参考に下のようなコードをサンプルとして用意してみました。

    class Program
    {
        static async Task Main(string[] args)
        {
            Task<string> task = HeavyMethod1();
            HeavyMethod2();
            Console.WriteLine(task.Result);
            Console.ReadLine();
        }

        static async Task<string> HeavyMethod1()
        {
            Console.WriteLine("すごく重い処理その1(´・ω・`)はじまり");
            await Task.Delay(5000);
            Console.WriteLine("すごく重い処理その1(´・ω・`)おわり");

            for(int i = 0; i < 100; i++)//100秒ThreadSpeep
            {
                Thread.Sleep(1000);
                Console.WriteLine(i + 1 + "秒経過");
            }
            Console.WriteLine("すっごくまった");
            return "hoge";
        }

        static void HeavyMethod2()
        {
            Console.WriteLine("すごく重い処理その2(´・ω・`)はじまり");
            Thread.Sleep(9000);
            Console.WriteLine("すごく重い処理その2(´・ω・`)おわり");
        }
    }
}


処理の流れとして
1.HeavyMethod1(以下HM1)が実行される
2.await Task.Delay(5000)で非同期処理開始 メソッドを抜ける
3.HeavyMethod2(以下HM2)が実行される
4.HM2のスリープ中にHM1のDelayが完了する HM1に処理が移る
ここまでは理解できます。
ここで、4でHM1に処理が移ったあとに、HM1でawaitを使わずに同期的な重い処理
があった場合、その処理が終わるまでHM2はどうなるのだろうと思い、HM1に100秒スリープする処理を追加しています。
結果をいうと、3秒経過時点でHM2の「すごく~おわり」の出力が挟まれます。
その後に4秒経過,5秒経過と続いていきます。
予想では、非同期処理するのはawaitの行なのでそれ以外は同期処理すると思ったですが、
(つまり100秒経過後にHM2に処理が戻ると予想した)結果は違いました。
この結果をみると関数そのものが非同期で動いているような気がするのですが、
どなたかこのもやもやを解消する答えを頂けないでしょうか?
つたない文で申し訳ないです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Zuishin

    2020/07/24 01:20

    > この間、裏でメソッドAは実行中であり、メソッドAの実行が完了すると、再びメソッドAに処理が戻ります。

    さっぱりわかりません。多分書いた人もわかっていません。その後の例示もひどいです。このサイトは参考にしないでください。

    キャンセル

  • BluOxy

    2020/07/24 04:18

    公式の記事があるので、こちらを見てはいかがでしょうか。
    https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/async/

    キャンセル

回答 2

checkベストアンサー

+3

Zuishin さんが質問のコメント欄で書かれている通り、参考にしている記事がよくないと思います。良くない最たるところは「非同期処理」と言いながら Main からは HeavyMethod1 も  HeavyMethod2 も非同期呼び出ししてないところだと思います。

どう動いているかを以下のコードで調べてみました。コードの内容は ManagedThreadId を表示するようにしたことと、ループを 100 回から 10 回に減らした以外は質問のコードと同じです。

イメージ説明

上の画像の青枠部分のコードは HeavyMethod1 も  HeavyMethod2 も非同期呼び出ししていません。赤枠部分の await Task.Delay(5000); も問題で、ここでスレッドが切り替わってしまいます。上のコードの事項結果は以下のようになります。各行の最後の数字が ManagedThreadId です。 

イメージ説明

ちなみに、赤枠部分の await Task.Delay(5000); をコメントアウトすると実行結果は以下のようになります。ManagedThreadId が 1 で変わらないのが分かりますか。同期実行ですがこちらの方が質問者さんの望む結果に近いのでは?

イメージ説明

赤枠部分の await Task.Delay(5000); はそのままとしておき、HeavyMethod1 を非同期で実行してみます。上のコードの画像で青枠の部分をその下のコメントアウトしたコードと入れ替えて実行してみます。結果は以下のようになります。これも望む結果になっていないのでは?

イメージ説明

他のサンプルを探した方がよさそうな気がします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/25 09:22

    大変詳しく詳細を書いて頂きありがとうございます。
    >>ManagedThreadId が 1 で変わらないのが分かりますか。同期実行ですがこちらの方が質問者さんの望む結果に近いのでは?
    「処理その2 はじまり」が「処理その1 はじまり」の直後に出るものとして、実行されるスレッドのイメージに関してはまさにこのように予想していました..
    つまるところ、僕が質問させていただいた「処理その2 おわり」の文が経過時間表示に割り込むのは、
    HeavyMethod1の中で、await以降のコードも全て別スレッドで実行されているため、スレッド1で実行されているHavyMethod2に影響を与えないからという認識でよろしいでしょうか?
    そうなってしまう理由なども知りたいところですが、間違った理解の上でこういった質問をすると余計に理解を妨げる気がしてしまうので、改めてBluOxyさんに教えていただいた公式の解説などそういったもので、ある程度勉強して、また詰まったら質問してみようと思います..

    キャンセル

  • 2020/07/25 11:22

    > つまるところ、僕が質問させていただいた「処理その2 おわり」の文が経過時間表示に割り込むのは、HeavyMethod1の中で、await以降のコードも全て別スレッドで実行されているため、スレッド1で実行されているHavyMethod2に影響を与えないからという認識でよろしいでしょうか?

    自分的には、HeavyMethod1 の中の await Task.Delay(5000); によって結果的に(図らずも?)マルチスレッドプログラミングとなって HeavyMethod1 と HeavyMethod2 が別スレッドで実行されることになったから、各々独自に決められたタイミングで Console.WriteLine メソッドを実行した結果・・・と言った方がピンときます。

    キャンセル

+2

>この結果をみると関数そのものが非同期で動いているような気がするのですが、

その通りです。
await は、非同期メソッドの「完了を待ち」を行うための構文です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る