2016-09-26 12 views
8

ASP.NET MVC 응용 프로그램에서 사용되는 다음 코드 예제가 있습니다. 이 코드의 목적은 장기간 실행되는 작업을 대기열에 넣기위한 "화재 및 잊어"요청을 만드는 것입니다. 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에서 정보를 가져 오는 것이 더 간단하지 않겠습니까? (나는 이것이 당신의 질문에 대답하지 않는다는 것을 깨닫는다. 그러나 나는 전체 컨텍스트의 필요성에 관해서 궁금하다.) – vcsjones

+2

맞습니다.이 방법은 정확하지만 유감 스럽지만 현재 변경할 수 없습니다. 우리 코드에는 HttpContext.Current에 대한 액세스 권한이 비즈니스 로직 깊숙한 곳에 있으며 현재 변경되지 않은 큰 노력이 있습니다. –

+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 속성"이 시작 스레드 "HttpContext 속성"에 대한 참조임을 알 수 있습니다. - 지금까지는 그렇게 좋았습니다 (초보 스레드가 끝난 후 모든 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가 Thread Id로 컨텍스트를 관리하기 때문에 두 개의 요청이 동일한 Id를 가진 Thread를 수신하면 HttpContext.Current는 두 개의 요청에서 동일한 객체에 대한 참조를 반환합니다. –