質問

私はまだ学んでいます async/await, 、だから私は明白な何かを求めている場合は、私を許してください。次の例を考えてみましょう:

class Program  {

    static void Main(string[] args) {
        var result = FooAsync().Result;
        Console.WriteLine(result);
    }

    static async Task<int> FooAsync() {

        var t1 = Method1Async();
        var t2 = Method2Async();

        var result1 = await t1;
        var result2 = await t2;

        return result1 + result2;

    }

    static Task<int> Method1Async() {
        return Task.Run(
            () => {
                Thread.Sleep(1000);
                return 11;
            }
        );
    }

    static Task<int> Method2Async() {
        return Task.Run(
            () => {
                Thread.Sleep(1000);
                return 22;
            }
        );
    }

}

これは期待どおりに動作し、コンソールに"33"が出力されます。

私が交換した場合 第二 await 明示的な待機を使用します。..

static async Task<int> FooAsync() {

    var t1 = Method1Async();
    var t2 = Method2Async();

    var result1 = await t1;
    var result2 = t2.Result;

    return result1 + result2;

}

...私は同じ動作を得るようです。

これらの2つの例は完全に同等ですか?

この場合、それらが同等である場合、最後のものを置き換える他のケースはありますか await 明示的な待機によって違いが生じるでしょうか?

役に立ちましたか?

解決 3

OK、私はこれを合計させて、これまでに提供された答えよりも完全な説明になるのは、それを合計させてください。

短時間

2番目のawaitを明示的な待機に置き換えると、コンソールアプリケーションに効果がかかりませんが、WAITの間、WPFまたはWinFormsアプリケーションのUIスレッドをブロックします。

また、例外処理はわずかに異なる(Stephen Clearyによって説明したように)。

長時間

ナットシェルでは、awaitはこれを行います。

  1. 待望のタスクがすでに終了した場合は、その結果を取得して続行します。
  2. 存在しない場合は、 posts 継続(awaitの後のメソッドの残りの部分)への現在の同期コンテキスト、1つがある場合。基本的に、awaitは私たちが始めた場所に私達に返却しようとしています。
    • 現在のコンテキストがない場合は、オリジナルの taskscheduler 。通常はスレッドプールです。

      2番目(&3番目など...)awaitは同じです。

      コンソールアプリケーションは通常同期コンテキストがないため、通常はスレッドプールによって扱われるため、継続の中でブロックされていれば問題はありません。 一方、

      WinFormsまたはWPFは、それらのメッセージループの上に同期コンテキストを実装しています。したがって、UIスレッド上で実行されるawaitは、(最終的に)UIスレッド上でその継続を実行します。継続でブロックしている場合は、メッセージループをブロックし、ブロック解除するまでUIを非対応させます。 Otoh、awaitの場合、UIスレッドをブロックすることなく、最終的にUIスレッド上で最終的に実行されるように継続を継続的に投稿します。

      次のWinFormsフォームには、awaitを使用して、UIを常に応答しています(クリックハンドラの前のasync):

      public partial class Form1 : Form {
      
          public Form1() {
              InitializeComponent();
          }
      
          private async void button1_Click(object sender, EventArgs e) {
              var result = await FooAsync();
              label1.Text = result.ToString();
          }
      
          static async Task<int> FooAsync() {
      
              var t1 = Method1Async();
              var t2 = Method2Async();
      
              var result1 = await t1;
              var result2 = await t2;
      
              return result1 + result2;
      
          }
      
          static Task<int> Method1Async() {
              return Task.Run(
                  () => {
                      Thread.Sleep(3000);
                      return 11;
                  }
              );
          }
      
          static Task<int> Method2Async() {
              return Task.Run(
                  () => {
                      Thread.Sleep(5000);
                      return 22;
                  }
              );
          }
      
      }
      
      .

      awaitのjerfodicetagcodeの2番目のFooAsyncを置き換えた場合、ボタンクリック後約3秒間応答してから、約2秒間フリーズします。

      • 最初のt2.Resultの後の継続は、awaitタスクが終了した後、約3秒後に発生する可能性があるUIスレッド上でそのターンをスケジュールするように順応します。
      • Method1Async()タスクが終了するまで、t2.ResultがUIスレッドをブロックすると、約2秒後にUIスレッドをブロックします。

        Method2Async()の前にasyncを削除し、button1_Clickawaitに置き換えた場合、デッドロック:

他のヒント

あなたの交換バージョンは、タスクが終了するのを待っている通話スレッドをブロックします。メインで意図的にブロックするので、そのようなコンソールアプリに目に見える違いを見るのは難しいですが、それらは間違いなく同等ではありません。

それらは同等ではありません。

Task.Result 結果が利用可能になるまでブロックします。私のブログで説明しているように、これは デッドロックを引き起こす可能性があります あなたが持っている場合 async 排他的アクセスを必要とするコンテキスト(例えば、UIまたはASP.NET アプリ)。

また、, Task.Result 例外をラップします AggregateException, したがって、同期的にブロックするとエラー処理が難しくなります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top