2016-09-26 5 views
8

次のコード例は、ASP.NET MVCアプリケーションで使用されています。 このコードの目的は、長時間実行されている操作をキューに入れるための「fire and forget」要求を作成することです。ASP.NET HttpContext.Current Task.Run内

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の内部知識は私には正しく理解できません。

ありがとうございます!

+3

HttpContext全体を渡すのではなく、必要なHttpContextから情報を取得する方が簡単ではないですか? (これはあなたの質問に答えることはできませんが、私はContext全体の必要性について興味があります)。 – vcsjones

+2

これは正しい方法ですが、残念ながら現在は変更できません。私たちのコードはHttpContextにアクセスしています。現在のビジネスロジックの真中にあり、それを変更することは、現在のところ我々にはない大きな努力です。 –

+1

あなたの質問には関係ありません。ウェブコンテキストで長時間実行されているタスクは素晴らしい考えではありません。サーバーは再起動でき、プール内には非常に多くのスレッドしかありません。あなたはHangFireやQuartzのようなものを考えましたか? –

答えて

5

は、私はあなたに少し内部をバンプしてみましょう:

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 property"は、スタータースレッド "HttpContext property"を参照していることを伝えています - これまでのスタータースレッドが終了した後に直面するNullReference/Dispose例外もあります。不思議 - あなたの

//Other long running code here. 

の内側にあなたが

var foo = await Bar(); 

のような文を持っている場合、問題はあなたが待ってヒットしたら、あなたの現在のスレッドがプールをスレッドに戻り、IOが終了した後、あなたがスレッドプールから新しいスレッドをつかむですその "HttpContextプロパティ"は何ですか?私は知らない:)ほとんどの場合、NullReferenceExceptionで終わるだろう。

+0

詳細な回答ありがとうございます!あなたが書いたことから、私は技術的にそれが起こる可能性があることを理解しています(Request2はRequest1のコンテキストを取得します)が、実際にはRequest1が終了し、ASP.NETがコンテキストをクリアしているため、おそらくnull参照を取得して例外を処理します。私はあなたの答えを正しく理解していますか?ありがとう! –

2

ここで実行する問題は、要求が完了したときにHttpContextが破棄されることです。 Task.Runの結果を待っているわけではないので、HttpContextの廃棄とタスク内での使用の間に競合条件を本質的に作り出しています。

あなたのタスクが実行される唯一の問題は、NullReferenceExceptionまたはObjectDisposedExceptionです。私はあなたが誤って別のリクエストのコンテキストを盗むことのできる方法を見ません。

また、あなたのタスク内で例外をログに記録しない限り、あなたの火と忘れがスローされ、あなたは決してそれについて知ることができません。

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

+0

答えをありがとう。競合状態と可能なNullReference/Disposed例外について知っています。しかし、なぜ私は知っているように、技術的にHttpContext.CurrentはスレッドIDによってコンテキストを管理しているので、同じ要求を2つのスレッドが受け取ると、そのHttpContext.Currentは2つの要求から同じオブジェクトへの参照を返します。 –

関連する問題