20

次のことを考えてみてください(デフォルトのMVCテンプレートに基づく)。これは、バックグラウンドで発生するいくつかの「もの」の簡略化されたバージョンです。正常に完了し、期待される結果を示します20:

public ActionResult Index()
{
    var task = SlowDouble(10);
    string result;
    if (task.Wait(2000))
    {
        result = task.Result.ToString();
    }
    else
    {
        result = "timeout";
    }

    ViewBag.Message = result;
    return View();
}
internal static Task<long> SlowDouble(long val)
{
    TaskCompletionSource<long> result = new TaskCompletionSource<long>();
    ThreadPool.QueueUserWorkItem(delegate
    {
        Thread.Sleep(50);
        result.SetResult(val * 2);
    });
    return result.Task;
}

ただし、ここでいくつかasyncをミックスに追加すると、次のようになります。

public static async Task<long> IndirectSlowDouble(long val)
{
    long result = await SlowDouble(val);

    return result;
}

ルートの最初の行を次のように変更します。

var task = IndirectSlowDouble(10);

その後、それは機能しません。代わりにタイムアウトします。ブレークポイントを追加するreturn result;と、メソッド内はルートがすでに完了したasyncにのみ発生します。基本的に、システムはリクエストが終了するまで、スレッドを使用して操作を再開することを望まないようです。さらに悪いことに、使用した(またはアクセスした)場合は、完全にデッドロックになります。async.Wait().Result

だから:それは何ですか?明らかな回避策は「関与しない」ですasyncが、ライブラリなどを使用する場合は簡単ではありません。最終的には、との間に機能的な違いはありません(構造的な違いは明らかですが)。SlowDoubleIndirectSlowDouble

注:コンソール/winformなどでまったく同じことが正常に機能します。

4

3 に答える 3

11

これは、ASP.NET(Pre .NET 4.5)で同期コンテキストを実装する方法と関係があります。この動作についてはたくさんの質問があります:

Task.WaitAllがASP.NETで複数の待機可能なタスクでハングしている

Asp.netSynchronizationContextは非同期継続のためにHttpApplicationをロックしますか?

ASP.NET 4.5には、この記事で説明されている同期コンテキストの新しい実装があります。

http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-httpruntime-targetframework.aspx

于 2012-11-29T09:15:05.900 に答える
8

使用する場合、本質的にブロックしている.Resultため、常にデッドロックの可能性があります。.Resultデッドロックを回避する方法は、タスクをブロックしないことです(使用する必要がasyncありますawait)。主題はここで詳細に説明されています:

1つの修正は、次を追加することConfigureAwaitです。

public static async Task<long> IndirectSlowDouble(long val)
{
    long result = await SlowDouble(val).ConfigureAwait(false);

    return result;
}
于 2012-11-29T09:05:58.367 に答える
6

別の修正はasync/await全体を使用することです:

public async Task<ActionResult> Index()
{
    var task = IndirectSlowDouble(10);
    long result = await task;
    ViewBag.Message = result.ToString();
    return View();
}
于 2012-11-29T09:18:00.910 に答える