请考虑以下情况:

  • 异步.ashx处理程序
  • 一种异步.asmx Web服务方法
  • 同步MVC 5 Controller 操作方法

  • 我正在尝试找出一种设置“逻辑线程”特定数据的方法,该数据可以在“逻辑” http请求期间一致地访问,即,如果该数据是在任何异步处理程序的“BeginExecute”部分中的线程上设置的您会认为,即使ASP.NET在其他OS/.Net线程上执行“EndExecute”部分,该asnc处理程序的“EndExecute”部分中的数据仍然可用。

    而且,我期望如果第二个请求被分配了先前分配给第一个http请求的线程,则在随后的http请求上,无论其处于哪个OS/.Net线程上的“BeginExecute”部分中的数据集都不可用。它在“BeginExecute”部分中,但是随着第一个http请求进入其异步操作(并且可能仍在完成其异步操作)时,此线程释放了。

    我相信.Net中的“逻辑线程”或“逻辑线程上下文”一词实际上意味着与我已经提到的相同的“逻辑”操作流程(而不是不断重新分配的底层OS/.Net线程)。如果从工作流的角度来看它,则每个http请求都是一个新的“逻辑”操作(即使多个用户依次或并行调用同一Web服务,每个请求也是一个新的分离的逻辑操作),在这种情况下意思是,“逻辑”操作是一次性的,不能重复。但是,相同的基础OS/.Net线程在到达时可以根据其可用性映射为“逻辑”操作。

    另外,我想将此数据公开为HttpContext.Current静态属性。对于某些人来说,这可能令人惊讶,但是HttpContext.Current如果您使用的是异步.asmx Web服务方法,则无法正常工作。我确定我已经在Web上阅读了内容HttpContext.Current应该总是返回正确的HttpContext,但是我已经在.asmx Web方法的EndExecuteMethod中将其视为null。如果有人可以确认我的发言是否正确,那就太好了,但这不是我要在这里提出的全部问题。

    阅读了大量文献(例如What is the difference between log4net.ThreadContext and log4net.LogicalThreadContext?http://msmvps.com/blogs/jon_skeet/archive/2010/11/08/the-importance-of-context-and-a-question-of-explicitness.aspxhttp://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html等,包括MSDN文档)后,以下是我的推断:
  • ThreadStatic对于底层OS/.Net线程是本地的,而不是“逻辑”操作的本地,因此在我的示例中;如果第二个http请求被分配了与第一个线程“BeginExecute”相同的线程,则在下一个http请求中将看到在“BeginExecute”中的第一个http请求上设置的数据。如果恰好由.Net将这些数据重新分配给另一个线程,则该数据将在“EndExecute”中不可用(在大多数情况下会发生)。
  • 对于我的用例,
  • Thread.SetData甚至有更多问题。它需要传递数据插槽,如果我要从Thread.GetNamedDataSlot的返回值传递数据插槽,则该信息在整个应用程序域中都可用;因为命名数据插槽在线程之间共享。
  • CallContext.SetData就像ThreadStatic(这意味着它不被应用程序域共享,但是如果不同的http请求被关联到相同的基础OS/.Net线程,它们将看到相同的数据)。 CallContext.SetData提供了编码(marshal)RPC调用的上下文数据的附加功能,该功能与当前提出的问题无关。
  • 然后是ThreadLocal类(.Net 4/.Net 4.5)。它似乎可以解决我的问题的一部分,我可以将它传递给BeingExecute操作的stateObject内,并从endExecute操作的相同stateObject参数中提取出来。从这个角度来看,ThreadLocal似乎是为.Net的异步支持而编写的。但是,当我需要像HttpContext.Current这样访问它时,它将无法工作,因为我无法看到保留它的“逻辑线程静态”实例的实例(除非我在前三点中有错误的说法)。
  • 最后,似乎CallContext.LogicalSetData完成了我打算达到的目标。使用CallContext.LogicalSetData和CallContext.LogicalGetData方法集,我应该能够实现HttpContext.Current之类的影响,该影响对于“逻辑任务执行”正确地起作用。

  • 现在出现问题:
  • 我上面所说的一切正确吗?请更正我提出的所有错误主张。
  • 我错过了.Net中是否有其他选项可用于线程静态类型的功能。
  • CallContext.LogicalSetData/LogicalGetData是否将上下文数据传递到RPC调用(msdn页面未明确提及http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext.logicalsetdata(v=vs.110).aspx)。
  • 使用CallContext.LogicalSetData/LogicalGetData是否有不利之处(无论是性能还是其他方面)。
  • 本页介绍LogicalSetData的写时复制行为:http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html。在异步处理程序/异步MVC 5操作方法的上下文中,如果我使用逻辑集数据保存引用类型并稍后更改引用类型的状态,将会产生什么影响。什么是惯例。
  • 对于mutation/logicalsetdata/async,我仍然无法通过对对象进行突变来解决问题。当异步方法启动时,写时复制行为将在下次调用逻辑集数据时触发上下文数据的副本。这是一个浅拷贝,因此我的引用对象现在实际上由2个逻辑上下文共享,并且一个上下文中的更改在另一个上下文中可见,这是我通常希望从引用类型得到的结果。

  • 一个漫长的问题,有很多引用文献,但希望我做得很好,并且答案也会对其他人有益。

    最佳答案



    唯一可能的选项是HttpContext.Current.Items和逻辑CallContext



    始终会在收到新请求时清除HttpContext.Current.Items,但您必须自己清除逻辑CallContext数据。



    我觉得这很奇怪。我还没有尝试过,但是它应该可以工作-如果您是在.NET 4.5上运行,以.NET 4.5为目标(即在targetFramework中将4.5设置为web.config),并且没有使用async void
    [ThreadStatic],线程本地数据插槽,(非逻辑)CallContextThreadLocal都是特定于线程的数据,不适用于异步代码。



    您的问题中确实有太多文字。堆栈溢出是一个问答站点,而不是指导站点。



    不。



    我不知道。试试看。



    有一定的性能影响。 .NET框架针对常见情况(无逻辑调用上下文数据)进行了高度优化。



    逻辑CallContext具有写浅复制行为。因此,任何类型的异步fork/join并发(即Task.WhenAll)最终都会共享该状态。如果您使用ConfigureAwait(false),那么您也可能会遇到竞争条件。

    为了真正解决您的问题,我建议您首先研究为什么HttpContext.Current无法按预期工作;我的猜测(没有看到项目)是targetFramework设置为4.0而不是4.5。如果可以使用HttpContext.Current.Items,则它是性能最高的选择。

    10-02 01:50
    查看更多