web-dev-qa-db-ja.com

TaskCompletionSource-スレッドレス非同期作業を理解しようとしています

TaskCompletionSourceの目的と非同期/スレッドレス作業との関係を理解し​​ようとしています。一般的な考えはあると思いますが、理解が正しいことを確認したい.

最初に、タスクパラレルライブラリ(TPL)を調べて、独自のスレッドレス/非同期作業(たとえば、ASP.NETサイトのスケーラビリティを向上させようとしている)を作成するための適切な方法とTPLの理解があるかどうかを判断しました将来的に非常に重要になるようです(async/await)。これが私をTaskCompletionSourceに導きました。

私の理解では、クラスの1つにTaskCompletionSourceを追加することは、コーディングを非同期にすることほど効果がないようです。それでも同期コードを実行している場合は、コードの呼び出しがブロックされます。これはMicrosoft APIにも当てはまると思います。たとえば、DownloadStringTaskAsyncクラスのWebClientをオフにすると、最初に実行しているセットアップ/同期コードはすべてブロックされます。実行しているコードは、現在のスレッドまたは新しいスレッドをスピンオフする必要があるスレッドで実行する必要があります。

そのため、Microsoftから他のTaskCompletionSource呼び出しを呼び出すときに、独自のコードでasyncを使用して、クラスのクライアントがクラスの新しいスレッドを作成してブロックしないようにする必要があります。

Microsoftが内部的に非同期APIをどのように実行しているかはわかりません。たとえば、.Net 4.5のasyncからの新しいSqlDataReaderメソッドがあります。 IO Completion Portsがあることを知っています。これは、おそらくほとんどのC#開発者が使用しない下位レベルの抽象化(C++?)だと思います。IO =完了ポートは、データベースまたはネットワーク呼び出し(HTTP)で機能するか、またはファイルIOで使用される場合にのみ機能します。

質問は、私の理解は正しいですか?私が誤って表現した特定の事柄はありますか?

35
coding4fun

TaskCompletionSourceは、コードを実行しないTaskオブジェクトを作成するために使用されます。

これらは、Microsoftの新しい非同期APIによってかなり使用されています。I/ Oベースの非同期操作(またはタイムアウトのような他の非CPUベースの非同期操作)があるときはいつでもです。また、async Task記述したメソッドはTCSを使用して、返されたTaskを完了します。

Taskインスタンスを作成するさまざまな方法を説明するブログ投稿 Creating Tasks があります。これはasync/awaitの観点から(TPLの観点ではなく)書かれていますが、ここでも適用されます。

また、Stephen Toubの優れた投稿もご覧ください。

59
Stephen Cleary

http://tutorials.csharp-online.net/TaskCompletionSource で提供された説明が好きです

(申し訳ありませんが、リンクは現在死んでいる可能性があります)

最初の2つの段落は以下のとおりです

Task.Runがプールされた(またはプールされていない)スレッドでデリゲートを実行するタスクを作成する方法を見てきました。タスクを作成する別の方法は、TaskCompletionSourceを使用することです。

TaskCompletionSourceを使用すると、開始してしばらくしてから完了するすべての操作からタスクを作成できます。これは、手動で駆動する「スレーブ」タスクを提供することによって機能します。これは、操作がいつ終了するか、または失敗するかを示すことによって行われます。これは、I/Oバウンドの作業に最適です。操作中にスレッドをブロックすることなく、タスクのすべての利点(戻り値、例外、および継続を伝達する機能)を利用できます。

TaskCompletionSourceを使用するには、クラスをインスタンス化するだけです。他のタスクと同様に、待機して継続をアタッチできるタスクを返すTaskプロパティを公開します。ただし、タスクは、次のメソッドを介してTaskCompletionSourceオブジェクトによって完全に制御されます。

public class TaskCompletionSource<TResult> 
{ 
 public void SetResult(TResult result); 
 public void SetException (Exception exception); 

 public void SetCanceled();   
 public bool TrySetResult (TResult result); 
 public bool TrySetException (Exception exception); 
 public bool TrySetCanceled();
 ... 
}

これらのメソッドのいずれかを呼び出すと、タスクに信号が送られ、完了、障害、またはキャンセルの状態になります(後者については、「キャンセル」のセクションで説明します)。これらのメソッドのいずれかを1回だけ呼び出す必要があります。もう一度呼び出すと、SetResult、SetException、またはSetCanceledは例外をスローしますが、Try *メソッドはfalseを返します。

次の例では、5秒間待機した後に42を出力します。

var tcs = new TaskCompletionSource<int>();
new Thread (() =>     {
                       Thread.Sleep (5000); 
                       tcs.SetResult (42); 
                      })    
           .Start();   
Task<int> task = tcs.Task;    // Our "slave" task. 
Console.WriteLine(task.Result);  // 42

その他の興味深い引用

TaskCompletionSourceの本当の力は、スレッドを拘束しないタスクを作成することです。

..以降

スレッドなしでTaskCompletionSourceを使用するということは、継続が開始されたとき(5秒後)にのみスレッドが関与することを意味します。エラーや過剰なリソースの消費なしに、これらの操作を10,000個同時に開始することで、これを実証できます。

6
Maxim Eliseev