2015-11-26 4 views
5

ベストプラクティスは、asyncコールをループ内のコレクションに収集し、Task.WhenAll()を実行することです。しかし、ループ内でawaitが発生した場合に何が起こるかを知りたい場合は、返されるTaskには何が含まれますか?さらにasyncはどうしますか?新しいタスクを作成し、すでに返されたTaskに順次追加しますか?私が想定ステップは ループ内で、各非同期呼び出しは、タスクの続行を使用して返されたタスクにチェーンされますか?

  • count、ループ、条件が
  • を確認しながらコードが入り、ゼロに設定されている

    1. LoopAsync呼び出される

      private void CallLoopAsync() 
      { 
          var loopReturnedTask = LoopAsync(); 
      } 
      
      private async Task LoopAsync() 
      { 
          int count = 0; 
          while(count < 5) 
          { 
           await SomeNetworkCallAsync(); 
           count++; 
          } 
      } 
      

      以下のコードあたりのよう

    2. SomeNetworkCallAsyncが呼び出され、返されたタスクが待たれています
    3. 新しいタスク/ awaitableが作成された
    4. 新しいタスクが(CallLoopAsyncに戻す)

    、プロセスが生活するのに十分な時間が与えられていれば、どのように/どのようにして、のように次のコード行はなりますcount++さらにはSomeNetworkCallAsyncを実行しますか?

    アップデート - Jon HannaStephen Clearyに基づいて:

    だから一つのタスクがあり、そのタスクの実装はNetworkCallAsyncへ 5の呼び出しを伴うだろうが、ステート・マシンを使用することは、これらのタスクは必要 意味しますこれが機能するためには明示的に連鎖されてはいけません。これは、 の例では、ループ結果を破るかどうかを決定することができます。 タスクの結果などです。

    それらが連鎖していないが、各呼び出しは、我々は(状態m/C、awaiter.GetResult();で)awaitを使用していたとして完成する前の呼び出しを待ちます。あたかも5つの連続した呼び出しが行われたように振る舞い、それらは次々に実行されます(の後は、前の呼び出しではは完了しました)。これが本当であるならば、我々はどのように我々は非同期calls.For EXを構成しているにもう少し注意する必要があります:

    代わり

    private async Task SomeWorkAsync() 
    { 
        await SomeIndependentNetworkCall();// 2 sec to complete 
    
        var result1 = await GetDataFromNetworkCallAsync(); // 2 sec to complete 
        await PostDataToNetworkAsync(result1); // 2 sec to complete 
    } 
    

    を書くそれはとても

    private Task[] RefactoredSomeWorkAsync() 
    { 
        var task1 = SomeIndependentNetworkCall();// 2 sec to complete 
    
        var task2 = GetDataFromNetworkCallAsync() 
        .ContinueWith(result1 => PostDataToNetworkAsync(result1)).Unwrap();// 4 sec to complete 
    
        return new[] { task1, task2 }; 
    } 
    

    を記述する必要がありますRefactoredSomeWorkAsyncと言うことができるのは、並列処理の可能性があるため、2秒だけ速くなります。

    これは間違いありませんか? - はい。 "非同期にすべて"、 "すべての方法を蓄積する"と一緒に、 "が良い方法です。同様の議論はhere

    答えて

    2

    private struct LoopAsyncStateMachine : IAsyncStateMachine 
    { 
        public int _state; 
        public AsyncTaskMethodBuilder _builder; 
        public TestAsync _this; 
        public int _count; 
        private TaskAwaiter _awaiter; 
        void IAsyncStateMachine.MoveNext() 
        { 
        try 
        { 
         if (_state != 0) 
         { 
         _count = 0; 
         goto afterSetup; 
         } 
         TaskAwaiter awaiter = _awaiter; 
         _awaiter = default(TaskAwaiter); 
         _state = -1; 
        loopBack: 
         awaiter.GetResult(); 
         awaiter = default(TaskAwaiter); 
         _count++; 
        afterSetup: 
         if (_count < 5) 
         { 
         awaiter = _this.SomeNetworkCallAsync().GetAwaiter(); 
         if (!awaiter.IsCompleted) 
         { 
          _state = 0; 
          _awaiter = awaiter; 
          _builder.AwaitUnsafeOnCompleted<TaskAwaiter, TestAsync.LoopAsyncStateMachine>(ref awaiter, ref this); 
          return; 
         } 
         goto loopBack; 
         } 
         _state = -2; 
         _builder.SetResult(); 
        } 
        catch (Exception exception) 
        { 
         _state = -2; 
         _builder.SetException(exception); 
         return; 
        } 
        } 
        [DebuggerHidden] 
        void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0) 
        { 
        _builder.SetStateMachine(param0); 
        } 
    } 
    
    public Task LoopAsync() 
    { 
        LoopAsyncStateMachine stateMachine = new LoopAsyncStateMachine(); 
        stateMachine._this = this; 
        AsyncTaskMethodBuilder builder = AsyncTaskMethodBuilder.Create(); 
        stateMachine._builder = builder; 
        stateMachine._state = -1; 
        builder.Start(ref stateMachine); 
        return builder.Task; 
    } 
    

    を(上記のあなたはasyncを使用するときに何が起こるかに基づいており、その結果は、いくつかの余分な属性とともに、有効なC#クラス名やフィールド名にすることはできません。そのMoveNext()は完全に無関係ではありませんIEnumeratorを思い出させる場合は名前を使用することを除いてawaitawaitasyncを実装するIAsyncStateMachineを生成するメカニズムTaskは多くの点でyieldIEnumerator<T>をどのように生成するかに似ています)。

    結果はAsyncTaskMethodBuilderから来て(asyncが生成隠さstructに近い)LoopAsyncStateMachineを利用する単一のタスクです。そのMoveNext()メソッドは、タスクの開始時に最初に呼び出されます。それから、SomeNetworkCallAsyncに待機時間を使用します。すでに完了している場合は、次の段階に進みます(countなど)。それ以外の場合は、待ち時間をフィールドに格納します。その後の使用では、SomeNetworkCallAsync()タスクが返されたために呼び出され、結果が返されます(この場合は無効ですが、値が返された場合は値になります)。さらにループを試み、まだ完了していないタスクを待っているときに再び戻ります。

    最終的にcountに達したら、SetResult()をビルダーで呼び出し、LoopAsyncが返されたTaskの結果を設定します。

    だから1 Taskがあり、そのTaskの実装はNetworkCallAsyncに5つの呼び出しを伴うだろうが、ステート・マシンの使用は、これらのタスクを明示的に動作するように、このために連鎖させる必要がないことを意味します。これにより、例えば、タスクの結果に基づいてループを破るかどうかを決定することができます。 awaitasync方法最初収量は、それが返さ

    3

    カウントがゼロで、新しいタスクが原因のawaitを作成され、それはないでしょう

    番号を返すことがあります。その結果、結果を格納または返すことなく、単にasyncメソッドを呼び出します。 loopReturnedTaskの値は、SomeNetworkCallAsyncには関係なくLoopAsyncTaskを格納します。

    await SomeNetworkCallAsync(); // call, wait and forget the result 
    

    あなたはMSDN article on async\awaitを読むことをお勧めします。これらのキーワードが存在していなかった場合は、ビットのようなコードが必要になり、何をすべきかasyncawaitのようなコードを生成するために

    +0

    私はOPに説明しようとしている間、私は自分自身を混乱させてしまったことを認めなければなりません。だから私は他のすべてのテキストを削除することに決めました。 –

    +0

    私の質問は、最初のループの間にコントロールが呼び出し側に返されるため、特に4つのSomeNetworkCallAsync呼び出しのコンテキストで(LoopAsyncの)タスクに何が含まれるかについてです。 – Saravanan

    1

    Task(又はTask<T>)。これはではなく、awaitによって監視されているです。これは、asyncメソッドによって作成された全く異なるタスクです。 asyncステートマシンは、その寿命を制御します。Task

    返されたTaskをメソッド自体を表すものと見なす方法があります。返されたTaskは、メソッドの完了時にのみ完了します。メソッドが値を返す場合、その値はタスクの結果として設定されます。メソッドが例外をスローすると、その例外はステートマシンによって取得され、そのタスクに配置されます。

    したがって、返されたタスクに継続を追加する必要はありません。メソッドが完了するまで、返されたタスクは完了しません。 /どのような方法で、数++さらにSomeNetworkCallAsyncなどの次のコード行が実行されますどのように

    これは私のasync introポストにこれを説明しています。要約すると、メソッドawaitが呼び出されると、現在のコンテキスト(null以外の場合はSynchronizationContext.Current)が取得されます。その場合はTaskScheduler.Currentが使用されます。 awaitが完了すると、そのコンテキスト内でそのasyncメソッドの実行を再開します。

    これは技術的に起こります。しかし、大多数の場合、これは単に意味:

    • async方法は、UIスレッド上で起動した場合、それはその同じUIスレッド上で再開します。
    • ASP.NETリクエストコンテキスト内でasyncメソッドが開始された場合、同じリクエストコンテキスト(必ずしも同じスレッド上にあるわけではありません)で再開されます。
    • それ以外の場合、asyncメソッドはスレッドプールスレッドで再開します。
    関連する問題