C#5-非同期/最初から待つことについて

最近リリースされたVisual Studio 11 Betaには、 async / awaitを使用した将来のC#5非同期プログラミングの新しい主要な機能があります。 Habréを含む多くの記事がすでに彼女について書かれています-例えば、このシリーズの記事 。 しかし、私にとっては、自分で試してみるまで、新しい構文の本質を理解できませんでした。 この記事は、このかなり興味深いツールを構造化して完全に扱い、結果をコミュニティと共有して、少し異なる方法で説明しようとする試みです。 さあ行こう...



なぜこれが必要なのですか?



C#は、各バージョンで積極的に開発されている言語であり、高品質で理解しやすいコードの作成を本当に簡素化する興味深い機能が登場します。 たとえば、Linqはコード内のコレクションおよびSQLクエリのフィルターの作成を大幅に簡素化しました。 ターンは非同期になりました。



フレームワークにはすでに、スレッドの手動作成からBackgroundWorkerのビジュアルコンポーネントまでのマルチスレッドコードを記述するかなり多数の方法が蓄積されていますが、非同期アプリケーションの開発には、かなり複雑なインフラストラクチャコードを記述する必要があります。 私の意見では、これの主な問題は、ユーザーインターフェースの状態をバックグラウンドスレッドから変更する必要があることです(ご存じのとおり、直接行うことはできません)。



新しいasync / await言語コンストラクトはこの問題を解決し、バックグラウンドスレッドでタスクを実行し、基本的に終了時にコードを実行できるだけでなく、明確にすることもできます-コードはほぼ同期して見えます(例外処理を含む)。



会う:async / await



一般的に言って、キーワードは本質を明確に反映していませんが、開発者が説明するように、誰もより良いアイデアを思い付かなかったので、それらは彼らが何であるかです。 プログラミングのほとんどのエンティティと同様に、新しい構造はコードから直接理解するのが最も簡単です。



//   private void OnButtonClick() { TextBox.Text = new WebClient().DownloadString("http://habrahabr.ru/"); } //   private async void OnButtonClick() { TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://habrahabr.ru/"); }
      
      







そして、同期バージョンですべてがシンプルで明確な場合、非同期バージョンで多くの疑問が生じます。 奇妙なことに、WebClientクラスの新しいメソッドであるDownloadStringTaskAsyncから開始します。このメソッドはTask <string>を返し、新しいスタジオで待機可能としてマークされます。 戻り値の型は、このストーリーの重要なポイントです。先を見て、awaitはTaskとTask <T>を返す関数でのみ機能すると言う価値があります。

UPD: jack128がコメントで正しく指摘さているように、awaitは、調整のおかげでGetAwaiter()メソッドを持つオブジェクトで機能します。



そのため、 DownloadStringTaskAsyncメソッドはタスクを作成し、関数からすぐにそれを返しますが、要求されたURLのページはバックグラウンドスレッドでダウンロードを開始します。 結果が完了するのを待って、Taskオブジェクトを直接手動で自由に操作できます。



 private void OnButtonClick() { Task<string> task = new WebClient().DownloadStringTaskAsync("http://microsoft.com/"); task.Wait(); //     ,    TextBox.Text = task.Result; }
      
      







メインスレッドではバックグラウンドでの実行を待機しているため、このコードはもちろん同期のままです...



タスク(Task <T>)を便利かつ非同期に処理する方法が必要です。これは、バックグラウンドスレッドに接続する唯一の「スレッド」のままです。 そして、ここに登場します-彼はTask <T>をTに拡張するだけでなく、メソッドの残りを「継続」に設定します。これは、タスクが完了した後、最も重要なのは同じスレッドで実行されます。 同時に、OnButtonClick()関数は終了し、アプリケーションは通常どおり動作し続けます-ユーザーのアクションに応答します。



バックグラウンドスレッドが作業を完了して結果を返すとすぐに、 メインスレッドで「継続」が実行され、この場合、テキストフィールドにページのコンテンツが設定されます。



asyncキーワードの処理は残っています。awaitが使用される関数をマークする必要があります。 すべてがシンプルであり、最も重要なことは、プログラムをコンパイルせずに、コンパイラがそれを忘れないように注意することです。



実際の動作



関数には複数の待機があり、非同期実行チェーンを作成できます。



 private async void StartButtonClick(object sender, RoutedEventArgs e) { //       StartButton.IsEnabled = false; //   ,      //       TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://habrahabr.ru/"); StatusLabel.Content = "  ,  "; //           var result = await Task<string>.Factory.StartNew(() => { Thread.Sleep(5000); //   ... return " "; }); //     StatusLabel.Content = result; StartButton.IsEnabled = true; }
      
      







例外はどうですか? 開発者は、新しい構文は単純で同期に近いものになると約束していたため、例外処理のこのような重要な問題を無視できませんでした。 約束どおり、バックグラウンドスレッドでスローされた例外は、古典的な構文を使用して処理できます(非同期性がないかのように)。



 private async void StartButtonClick(object sender, RoutedEventArgs e) { try { TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://not-habrahabr.ru/"); } catch (Exception ex) { MessageBox.Show(ex.Message); } }
      
      







ただし、例外処理では、理解する必要がある点が1つあります。awaitの後に来るすべてのコードは完了までインストールされ、実行されるタイミングが不明であるため、このような例外処理は機能しません。



 //   !!! private void StartButtonClick(object sender, RoutedEventArgs e) { try { Download(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } private async void Download() { TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://not-habrahabr.ru/"); }
      
      







ダウンロード関数は、バックグラウンドスレッドでタスクが作成されるとすぐに制御を返し、その後、StartButtonClick関数からの出口が実行されます...そして後でドメイン名を解決できないという例外がバックグラウンドスレッドでスローされます。 詳細な説明はこちらをご覧ください



合計



今後の.Net 4.5では、新しい構文をサポートするために多くのクラスが追加されます。 TaskおよびTask <T>を返す多くの関数が表示されます。 そして、新しい構文の単純さから判断すると、それは広く使用されます。したがって、新しい言語構成、それらのアクションとスコープの明確な理解が必要です。



要約すると、asyncキーワードはメソッドがバックグラウンドスレッドで(名前が示すように)実行されるのではなく、残りのメソッドが次のようにTaskおよびTask <T>で機能するメソッド内に待機があることにのみ注意してくださいawaitはタスクの完了時に実行されますが、メインスレッドで実行されます。



新しい構文の導入により、Microsoftは構文の砂糖だけでなく、例外処理を含む非同期プログラミングのパターン全体、および非同期関数の実行を中断し、実行の進行状況を通知する方法を提供します...これらは既にこの記事の範囲外です。



あいまいな場所が残っている場合は、コメントのすべての質問に答えようとします。



All Articles