2017-07-06 10 views
0

私は非同期操作からWindowsフォームプログレスバーを更新する方法を理解していますが、予期しない動作が発生しています。非同期メソッドから進行状況バーを更新します。

基本的には、進捗バーを更新するためにクリックした後、プログレスバーが100%更新された後に0に戻す必要があるボタンがあります。

これは私のコードです:

private async void button1_Click(object sender, EventArgs e) 
    { 
     await CallMethodAsync().ContinueWith((prevTask) => 
     { 
      prevTask.Wait(); 
      progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; })); 
     }); 
    } 

    private static async Task ExecuteMethodAsync(IProgress<double> progress = null) 
    { 
     double percentComplete = 0; 
     bool done = false; 

     while (!done) 
     { 
      if (progress != null) 
      { 
       progress.Report(percentComplete); 
      } 

      percentComplete += 10; 

      if(percentComplete == 100) 
      { 
       done = true; 
      } 
     } 
    } 

    private async Task CallMethodAsync() 
    { 
     var progress = new Progress<double>(); 
     progress.ProgressChanged += (sender, args) => { progressBar1.Increment(10); }; 
     await ExecuteMethodAsync(progress); 
    } 

は、プログレスバーが、私は「待って()」プログレスバーの値を更新する必要があり、操作を呼び出す場合でも、全く更新されていないこの実装を持ちます。

私はこのコードの一部削除した場合:

progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; })); 

をプログレスバーが更新されますが、それはそのようなすべての時間のまま、と私はそのように、それが完全に充填した後に0に戻ってそれを設定したいですもう一度ボタンをクリックすると再び更新できます。

誰かが私に間違っていることを説明してもらえますか?

+1

コンパイル警告* CS1998 C#この非同期メソッドには 'await'演算子がなく、同期的に実行されます。 。) 'バックグラウンドスレッドでCPUバインドされた作業を行う。あなたが間違っていることは、そのコンパイラの警告を引き起こし、警告はそれを修正する方法を指示します。 –

+1

また、[ContinueWithとasync](https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html)を混ぜ合わせてはいけません。(これで問題は発生しません) –

+1

https ://stackoverflow.com/questions/11500563/winform-multithreading-use-backgroundworker-or-not –

答えて

3

ContinueWithのような機能を使用してタスクを連結したときに一連の命令を実行するのが難しいため、async-await構文が発明された理由の1つです。

async-awaitを使用する場合は、ContinueWithのような文を使用することはめったにありません。 awaitの後、スレッドは待機した後にステートメントを使用して処理を続行します。

ボタンをクリックすると、ExcecuteMethodAsyncに電話します。この関数は、進行状況を定期的に報告したいので、IProgressをとります。この関数を非同期に呼び出すと、関数が何かを待つ必要があるときはいつでも、関数は実際には待機しませんが、制御を返して待っているのではなく、待っている以外のことを行うことができますあなたの呼び出し元は、待ち状態になるまで処理を続行します。

async-awaitの素晴らしいことは、async関数の呼び出しの後に続くスレッドが呼び出しスレッドと同じコンテキストを持つことです。つまり、元のスレッドと見なすことができます。次のようにInvokeRequiredなどミューテックスを使用してデータを保護する必要はありません

んがあなたの機能を簡素化することができなかった:ボタンがクリックされたときに

async Task CallMethodAsync() 
{ 
    var progress = new Progress<double>(); 
    progress.ProgressChanged += OnProgressReported; 

    await ExecuteMethodAsync(progress); 
} 

private void OnProgressReported(object sender, ...) 
{ 
    // because this thread has the context of the main thread no InvokeRequired! 
    this.progressBar1.Increment(...); 
} 

private async void button1_Click(object sender, EventArgs e) 
{ 
    await CallMethodAsync(); 
} 

ので、CallMethodAsyncが呼び出されます。この関数はA Progressオブジェクトを作成し、そのReportイベントにサブスクライブします。これはあなたのUIスレッドであることに注意してください。次に、ExecuteMethodAsyncを呼び出します。これは、OnProgressReportedによって処理されるイベントReportを定期的に発生します。

ExecuteMethodAsyncは非同期であるため、待機中の場所があることを確認できます。これは、待機する必要があるときはいつでも、制御は呼び出し側に戻ります。この場合は即時ですが、待ち受けが発生するまでCallMethodAsyncです。

コントロールは、これらのすべてのコントロールが同じ文脈を持つ

など、コントロールがコールスタックを上がるので、それはすぐに、のawaitに遭遇したのbutton1_Clickで、発信者、にコールスタックを上がる:それはようですそれらが同じスレッドである場合。

非同期のawaitを理解するために多くの非同期のためにどこかに途中でthis interview with Eric Lippert.検索で私を助けた記事は、ステファン・クリアリーによってもthis article by the ever so helpful Stephen ClearyAsync/Await - Best Practices in Asynchronous Programmingだった私には良いプラクティスを学ぶことがたくさん助けたもう一つのarticelは

を待つ

関連する問題