C#の非同期

継続リンク: パートIIおよびパートIII



PDC2010では、Heisbergは次のバージョンのC#が非同期コンピューティングを便利に整理するためのプリミティブをサポートすると発表しました。発表に加えて、スタジオ用のCTPバージョンの拡張機能( ダウンロード )が導入されました。



この改善は、非同期操作の組み合わせを容易にし、非同期コードをできるだけ同期に近づけるようにするために作成されました。 .NET FrameworkのSilverlightバージョンには、ネットワークを操作するための非同期モデルのみが含まれているため、この改善は非常に適切です。



単純化された非同期プログラミングはC#の革新であるという事実にもかかわらず、モナドに基づく非同期の実装はHaskell、F#、およびNemerleで行われているため、アプローチ自体を革新と呼ぶことはできません。 実際、モナド言語のサポートにより、さらに多くの実装が可能になるため、ハイスベルクのプレゼンテーションを見て驚いたのですが、この言語には特別なケースしか組み込まれていないことに気付きました。



非同期!



将来のC#での非同期プログラミングのサポートは、タスクおよびタスク<T>タイプ、待機演算子、非同期マーカーの3つの革新に基づいています。



タスクとタスク<T>


これらのタイプは、実行時の非同期計算とその結果を記述します。 スレッドで類推できます。 以下は、タスク<T>署名の一部です。



public class Task<TResult> : Task { public TResult Result { get; internal set; } public Task<TNewResult> ContinueWith<TNewResult>( Func<Task<TResult>, TNewResult> continuationFunction ); }
      
      





Task <string>を返すメソッドを呼び出したと想像してください。 ほとんどの場合、これは、呼び出されたメソッドが非同期操作を開始し、その結果が文字列であるため、制御を返すだけでなく、非同期操作自体を記述するオブジェクトが完了を待たずに返したことを意味します。 このオブジェクトを取得すると、Resultフィールドに移動して操作の結果を取得できます;この場合、現在のスレッドは非同期操作が完了するまで一時停止します。



Task <T>型のオブジェクトで実行できる別のアクションは、非同期操作が完了するとすぐに、操作の結果でcontinuationFunctionを実行する必要があることを伝えることです。 したがって、複数の非同期操作を組み合わせて、非同期操作を取得します。



説明した型は.NET Framework 4で導入されたSystem.Threading.Tasks名前空間に属するため、原則として、このアプローチを使用して非同期計算を整理できます。ただし、1つの論理的な方法があるため、この使用はあまり便利ではありません:結果を取得それを処理するには、メソッドに分解する必要があります。1つは結果の受信を開始し、2つ目は結果の処理を開始することです。 したがって、非同期トークンとawait演算子が導入されました。



非同期および待機


トークンは、メソッドに適用される属性またはアクセス修飾子に非常に似ています。 以下に使用例を示します。



 public async Task<string> DownloadStringTaskSlowNetworkAsync(Uri address) {
      
      





トークンは、Task <T>またはvoidを返すメソッドに適用できます。 メソッド本体で他の非同期呼び出しの組み合わせが発生する場合(await演算子を使用)、またはメソッドが非同期操作を定義する場合、マーカーをメソッドに適用する必要がありますが、拡張機能で提供されるAsyncCtpLibrary.dllライブラリは既に使用する多数のメソッドを定義しているため、これはほとんど必要ありません基本的な非同期リクエスト。



軽量の非同期操作が基づいている最後のキーオブジェクトは、awaitステートメントです。 非同期操作のシーケンスを1つに結合する必要があります。 この演算子は、非同期操作を記述する入力オブジェクトを取得し、awaitを使用して式に続くすべてがクロージャーに変換され、awaitが適用されるオブジェクトのContinueWithメソッドの引数になるようにコードを書き換えます。 これは、コンパイラーへの指示であると言えます。「したがって、後続のすべては、現在の非同期操作が完了したらすぐに実行する必要があります。」 Resultプロパティへのアクセスとの違いは、現在の実行スレッドは一時停止されないが、非同期操作を記述するオブジェクトが作成され、すぐに戻ることです。



Webページを非同期にダウンロードして保存する例を考えてみましょう。ページは非同期にロードされますが、コードは同期的に見えることがわかります。



 class Program { static async Task SavePage(string file, string a) { using (var stream = File.AppendText(file)) { var html = await new WebClient().DownloadStringTaskAsync(a); stream.Write(html); } } static void Main(string[] args) { var task = SavePage("habrahabr", "http://habrahabr.ru"); task.Wait(); } }
      
      





async / awaitコンパイラは次のように書き換えます:



 static Task SavePage(string file, string a) { var stream = File.AppendText(file); return new WebClient().DownloadStringTaskAsync(a).ContinueWith(delegate(Task<string> data) { try { stream.Write(data.Result); } finally { stream.Dispose(); } }); }
      
      





もちろん、asyncメソッドには複数のawait呼び出しを含めることができるため、非同期操作を1つにまとめることができます。たとえば、Webページを保存する例は、わずかな変更でファイルへの非同期書き込みを使用して書き換えることができます。



 static async Task SavePage(string file, string a) { using (var stream = File.AppendText(file)) { var html = await new WebClient().DownloadStringTaskAsync(a); await stream.WriteAsync(html); } }
      
      





舞台裏に残っているもの


非同期操作(Task.WaitOne、Task.WaitAll)の同期ツール、およびデータフロー(System.Threading.Tasks.Dataflow)に基づくマルチスレッドアプリケーション、および操作の進行状況に関する情報については書きませんでした。 ただし、このリリースには、テクノロジを学習できる多数の例が含まれていることに注意してください。 それらを研究することをより面白くするためには、問題があります:DiningPhilosophersの例では、デッドロックがあり、理由を見つける必要があります=)



All Articles