4

私は理解していないと思います。私はそれを考えていました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正しいのですが、どうやらそうではありません。


  • 呼び出しConfigureAwaitあなたが実際にはない場合は無意味ですawaitTask。 - Servy
  • ありがとう@Servy私は一種のことを知っていましたが、それは私の心を滑らせました - George Mauer

2 답변


6

正しい並行作業を異なるスレッドにオフロードする方法は、使用することです。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質問に対する答えです。


  • これは正しい答えです実際の質問。私の答えは、OPのアプローチが良いものであるかどうかについてより多くの議論のポイントを持っています。 - rossipedia
  • 実は@rossipedia。繰り返しますが、これは実際のコードがどのように見えるべきではないということです。 - i3arnon
  • さて、4.5.2を展開するようにインフラに納得させることができるかどうかを判断してください。 - George Mauer
  • Task.Yield()タスクを返さないので、何もありません。ConfigureAwait関数。そのため、お勧めしない方法ではうまくいきません。 - George Mauer
  • @GeorgeMauerあなたは正しい、私は考えていません。私は答えに非常に興味深い解決策を追加しました。 - i3arnon

4

l3arnonの答え正しいものです。この答えは、OPによって提案されたアプローチが良いものであるかどうかについてのより多くの議論です。)

特別なことは何も必要ありません。のcreateFileFromLongRunningComputationメソッドには特別なことは何も必要ありません。awaitその中でいくつかの非同期メソッドとConfigureAwait(false) すべきデッドロックを回避するには、通常とは異なることをしていないと仮定します(おそらく、メソッド名を指定したファイル入出力のみです)。

警告

これは危険です。タスクが完了するのに時間がかかりすぎるならば、ASP.netはたぶんあなたの下からラグを引き出すでしょう。

コメンターの一人が指摘したように、これを達成するためのより良い方法があります。そのうちの一つはHostingEnvironment.QueueBackgroundWorkItem(.NET 4.5.2以上でのみ利用可能です)。

長時間の計算が完了するのにかなり長い時間がかかる場合は、おそらくそれを完全にASP.netの外にしておくほうが賢明です。そのような状況では、より良い方法は、ある種のメッセージキューと、それらのメッセージをIIS / ASP.netの外部で処理するサービスを使用することです。


  • @GeorgeMauerそれでは、なぜタイトルが複数のタスクを待っているのでしょうか。また、これは非常に危険です。あなたがasp.netでバックグラウンド処理をしたいならば、より良い方法があります。 - i3arnon
  • @GeorgeMauerあなたが必要とするすべてはここにあります:hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx - i3arnon
  • また関連 - blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html - Daniel Kelley
  • @GeorgeMauer:その場合は、ASP.NETでこれを行うべきではありません。独立したバックエンドワーカープロセスを持つ永続的なキューが必要です。 - Stephen Cleary
  • 彼はすでに非同期の仕事をしています。の呼び出しでそれらをラップする理由はありません。Run;スレッドプール内のタスクをスケジュールする時間を無駄にしているだけです。開始非同期操作 - Servy

リンクされた質問


関連する質問

最近の質問