私は理解していないと思います。私はそれを考えていましたTask.Yield()
タスクに対して新しいスレッド/コンテキストを開始するよう強制したこの答えをもう一度読むメソッドを非同期にするだけのようです。それはまだ同じ文脈にあります。
asp.netプロセスで、デッドロックを発生させることなく複数のタスクを並行して作成および実行する正しい方法は何ですか?
つまり、次のような方法があるとします。
async Task createFileFromLongRunningComputation(int input) {
//many levels of async code
}
そして、あるPOSTルートがヒットしたら、同時に上記のメソッドを3回起動したいです。すぐに戻るしかし、3つすべてが終わったらログに記録します。
私は私の行動にこのようなものを入れる必要があると思います
public IHttpAction Post() {
Task.WhenAll(
createFileFromLongRunningComputation(1),
createFileFromLongRunningComputation(2),
createFileFromLongRunningComputation(3)
).ContinueWith((Task t) =>
logger.Log("Computation completed")
).ConfigureAwait(false);
return Ok();
}
に入る必要があるものcreateFileFromLongRunningComputation
?私は思っていたTask.Yield
正しいのですが、どうやらそうではありません。
の正しい並行作業を異なるスレッドにオフロードする方法は、使用することです。Task.Run
ロシペディアが示唆したように。
バックグラウンド処理に最適なソリューションASP.Net(どこAppDomain
リサイクルすることができます/あなたのすべてのタスクと一緒に自動的にシャットダウンすることができますスコット・ヘンゼルマンそしてスティーブン・クリアリーのブログ(HangFireなど)
しかし、あなたはできた利用するTask.Yield
一緒にConfigureAwait(false)
同じことを達成するために。
すべてTask.Yield
メソッドの残りの部分が同期して進行しないことを確認するawaiterを返すことです。IsCompleted
戻るfalse
そしてOnCompleted
実行するAction
すぐにパラメータ)。ConfigureAwait(false)
無視しますSynchronizationContext
したがって、残りのメソッドを強制的に実行します。ThreadPool
糸。
両方を一緒に使用する場合は、async
メソッドはただちにタスクを返します。ThreadPool
糸のようなTask.Run
):
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Yield().ConfigureAwait(false);
// executed on a ThreadPool thread
}
編集する
ジョージ・マウアーはそれ以来Task.Yield
戻るYieldAwaitable
使えないConfigureAwait(false)
これは上の方法ですTask
クラス。
あなたはを使用して同様のことを達成することができますTask.Delay
非常に短いタイムアウトで、それは同期的ではないでしょうがあなたは多くの時間を無駄にしないでしょう:
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Delay(1).ConfigureAwait(false);
// executed on a ThreadPool thread
}
よりよい選択はaを作成することでしょうYieldAwaitable
それは単に無視しますSynchronizationContext
使用と同じConfigureAwait(false)
します:
async Task CreateFileFromLongRunningComputation(int input)
{
await new NoContextYieldAwaitable();
// executed on a ThreadPool thread
}
public struct NoContextYieldAwaitable
{
public NoContextYieldAwaiter GetAwaiter() { return new NoContextYieldAwaiter(); }
public struct NoContextYieldAwaiter : INotifyCompletion
{
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
var scheduler = TaskScheduler.Current;
if (scheduler == TaskScheduler.Default)
{
ThreadPool.QueueUserWorkItem(RunAction, continuation);
}
else
{
Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.PreferFairness, scheduler);
}
}
public void GetResult() { }
private static void RunAction(object state) { ((Action)state)(); }
}
}
これはお勧めではありません、それはあなたのTask.Yield質問に対する答えです。
Task.Yield()
タスクを返さないので、何もありません。ConfigureAwait
関数。そのため、お勧めしない方法ではうまくいきません。 - George Mauer
(l3arnonの答え正しいものです。この答えは、OPによって提案されたアプローチが良いものであるかどうかについてのより多くの議論です。)
特別なことは何も必要ありません。のcreateFileFromLongRunningComputation
メソッドには特別なことは何も必要ありません。await
その中でいくつかの非同期メソッドとConfigureAwait(false)
すべきデッドロックを回避するには、通常とは異なることをしていないと仮定します(おそらく、メソッド名を指定したファイル入出力のみです)。
警告:
これは危険です。タスクが完了するのに時間がかかりすぎるならば、ASP.netはたぶんあなたの下からラグを引き出すでしょう。
コメンターの一人が指摘したように、これを達成するためのより良い方法があります。そのうちの一つはHostingEnvironment.QueueBackgroundWorkItem
(.NET 4.5.2以上でのみ利用可能です)。
長時間の計算が完了するのにかなり長い時間がかかる場合は、おそらくそれを完全にASP.netの外にしておくほうが賢明です。そのような状況では、より良い方法は、ある種のメッセージキューと、それらのメッセージをIIS / ASP.netの外部で処理するサービスを使用することです。
Run
;スレッドプール内のタスクをスケジュールする時間を無駄にしているだけです。開始非同期操作 - Servy
ConfigureAwait
あなたが実際にはない場合は無意味ですawait
のTask
。 - Servy