web-dev-qa-db-ja.com

Task.Run内のASP.NETHttpContext.Current

ASP.NETMVCアプリケーションで使用される次のコード例があります。このコードの目的は、長時間実行されている操作をキューに入れるための「ファイアアンドフォーゲット」リクエストを作成することです。

public JsonResult SomeAction() {
   HttpContext ctx = HttpContext.Current;            

   Task.Run(() => {
       HttpContext.Current = ctx;
       //Other long running code here.
   });

   return Json("{ 'status': 'Work Queued' }");
}

これは非同期コードでHttpContext.Currentを処理するための良い方法ではないことを私は知っていますが、現在、私たちの実装では他のことを行うことができません。このコードがどれほど危険かを理解したい...

質問: Task.Run内でHttpContextを設定すると、コンテキストがまったく別のリクエストに設定される可能性は理論的にはありますか?

はいと思いますが、よくわかりません。私の理解:Request1はスレッドプールからのThread1で処理され、Thread1が絶対に別の要求(Request2)を処理している間、Task.Run内のコードはRequest1からRequest2にコンテキストを設定します。

私は間違っているかもしれませんが、ASP.NETの内部に関する知識では、正しく理解することができません。

ありがとう!

14
Alex Dn

私はあなたに少し内部をぶつけさせてください:

public static HttpContext Current
{
    get { return ContextBase.Current as HttpContext; }
    set { ContextBase.Current = value; }
}

internal class ContextBase
{
    internal static object Current
    {
        get { return CallContext.HostContext; }
        set { CallContext.HostContext = value; }
    }
}

public static object HostContext
{
    get 
    {
        var executionContextReader = Thread.CurrentThread.GetExecutionContextReader();
        object hostContext = executionContextReader.IllogicalCallContext.HostContext;
        if (hostContext == null)
        {
            hostContext = executionContextReader.LogicalCallContext.HostContext;
        }
        return hostContext;
   }
   set
   {
        var mutableExecutionContext = Thread.CurrentThread.GetMutableExecutionContext();
        if (value is ILogicalThreadAffinative)
        {
            mutableExecutionContext.IllogicalCallContext.HostContext = null;
            mutableExecutionContext.LogicalCallContext.HostContext = value;
            return;
        }
        mutableExecutionContext.IllogicalCallContext.HostContext = value;
        mutableExecutionContext.LogicalCallContext.HostContext = null;
   }
}

そう

var context = HttpContext.Current;

(擬似コード)に等しい

var context = CurrentThread.HttpContext;

そしてあなたのTask.Runの中でこのようなことが起こります

CurrentThread.HttpContext= context;

Task.Runは、スレッドプールからのスレッドで新しいタスクを開始します。つまり、新しいスレッド「HttpContextプロパティ」がスタータースレッド「HttpContextプロパティ」への参照であるということです。これまでのところ、これまでのところ良好です(スタータースレッドの終了後に発生するすべてのNullReference/Dispose例外も同様です)。問題はあなたの中にある場合です

//Other long running code here.

あなたは次のような声明を持っています

var foo = await Bar();

待機を押すと、現在のスレッドがスレッドプールに戻され、IOが終了した後、スレッドプールから新しいスレッドを取得します-その「HttpContextプロパティ」が何であるか疑問に思いますか?私はしません知っている:)おそらくあなたはNullReferenceExceptionで終わるでしょう。

11
Ondrej Svejdar

ここで遭遇する問題は、リクエストが完了するとHttpContextが破棄されることです。 Task.Runの結果を待っていないので、基本的にHttpContextの破棄とタスク内での使用の間に競合状態を作成しています。

タスクで発生する唯一の問題は、NullReferenceExceptionまたはObjectDisposedExceptionであると確信しています。別のリクエストのコンテキストを誤って盗む可能性がある方法は見当たりません。

また、タスク内で例外を処理してログに記録しない限り、ファイアアンドフォーゲットがスローされ、それについて知ることはできません。

HangFireを確認するか、別のプロセスからのバックエンドジョブを処理するためにメッセージキューを使用することを検討してください。

5
scottt732