メソッド内で async/await を使用したくないが、外部から await キーワードを使用できるように「装飾」する場合は、TaskCompletionSource.cs :
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
あちらこちらから_
タスクでこのようなパラダイムをサポートするには、タスク ファサードを保持し、任意の非同期操作をタスクとして参照する方法が必要ですが、そのタスクを提供している基盤となるインフラストラクチャのルールに従って、そのタスクの有効期間を制御する必要があります。非同期であり、それほどコストがかからない方法で実行します。これが TaskCompletionSource の目的です。
WebClient.csなどの .NET ソースでも使用されていることがわかりました。
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<string>(address);
// Setup the callback event handler
UploadStringCompletedEventHandler handler = null;
handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
this.UploadStringCompleted += handler;
// Start the async operation.
try { this.UploadStringAsync(address, method, data, tcs); }
catch
{
this.UploadStringCompleted -= handler;
throw;
}
// Return the task that represents the async operation
return tcs.Task;
}
最後に、次のことも役に立ちました。
私はいつもこの質問を受けます。つまり、外部リソースへの I/O 呼び出しをブロックしているスレッドがどこかにあるはずです。つまり、非同期コードは要求スレッドを解放しますが、それはシステム内の他の場所にある別のスレッドを犠牲にするだけですよね? いいえ、まったくありません。
非同期要求がスケーリングされる理由を理解するために、非同期 I/O 呼び出しの (簡略化された) 例をトレースします。リクエストがファイルに書き込む必要があるとしましょう。要求スレッドは、非同期書き込みメソッドを呼び出します。WriteAsync は Base Class Library (BCL) によって実装され、非同期 I/O に完了ポートを使用します。そのため、WriteAsync 呼び出しは、非同期ファイル書き込みとして OS に渡されます。次に、OS はドライバー スタックと通信し、データを渡して I/O 要求パケット (IRP) に書き込みます。
ここが興味深いところです。デバイス ドライバーが IRP をすぐに処理できない場合は、非同期的に処理する必要があります。そのため、ドライバーはディスクに書き込みを開始するように指示し、OS に「保留中」の応答を返します。OS はその「保留中」の応答を BCL に渡し、BCL は未完了のタスクを要求処理コードに返します。リクエスト処理コードはタスクを待機し、タスクはそのメソッドから不完全なタスクを返します。最後に、要求処理コードが ASP.NET に不完全なタスクを返し、要求スレッドが解放されてスレッド プールに戻ります。
ASP.NET での非同期/待機の概要
目標が (応答性ではなく) スケーラビリティの向上である場合、それを行う機会を提供する外部 I/O の存在にすべて依存します。