线程静态、ASP.NET和异步处理程序



请考虑这些sceanrios:

  1. 异步.ashx处理程序
  2. 异步.asmx web服务方法
  3. 一种同步MVC 5控制器动作方法

我正试图找到一种方法来设置"逻辑线程"特定的数据,这些数据可以在"逻辑"http请求期间一致访问,即,如果数据是在线程上的"BeginExecute"部分设置的,那么你会考虑哪个异步处理程序,即使ASP.NET在不同的OS/.NET线程上执行"EndExecute"部分,该数据也可以在asnc处理程序的"EndExecute"部分中使用。

此外,我预计,如果第二个请求被分配给了之前分配给第一个http请求的线程(当它处于"BeginExecute"部分时),那么它所在的任何OS/.Net线程上的"BeginExecute"部分中的数据集在随后的http请求中都不可用,但当第一个http要求进入其异步操作时,该线程释放了(可能仍在完成异步操作)。

我相信.Net中的"逻辑线程"或"逻辑线程上下文"实际上意味着与我提到的相同的"逻辑"操作流(而不是不断被重新分配的底层OS/.Net线程)。如果从工作流的角度来看,每个http请求都是一个新的"逻辑"操作(即使多个用户顺序或并行调用同一个web服务,每个请求都是新的、单独的逻辑操作),从这个意义上讲,"逻辑"操作是一次性的,不能重复。然而,相同的底层OS/.Net线程可以根据其可用性在到达时映射到"逻辑"操作。

此外,我想将这些数据公开为HttpContext.Current类型的静态属性。对一些人来说,这可能会让人感到惊讶,但如果您使用例如async.asmx web服务方法,HttpContext.Current将无法正常工作。我确信我读过网络上的内容,上面写着HttpContext.Current应该总是返回正确的HttpContext,但我在.asmx网络方法的EndExecuteMethod中看到它为null。如果有人能确认我最后一次发言是否正确,那就太好了,但这一发言并不是我想在这里提出的全部问题。

在阅读了大量的文献后(例如,log4net.ThreadContext和log4net.LogicalThreadContext之间有什么区别?,http://msmvps.com/blogs/jon_skeet/archive/2010/11/08/the-importance-of-context-and-a-question-of-explicitness.aspx,http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html以及更多MSDN文档),下面是我的推论:

  1. ThreadStatic是底层OS/.Net线程的本地线程,而不是"逻辑"操作,因此在我的示例中;如果第二个http请求被分配了与第一个线程的"BeginExecute"相同的线程,那么"BeginExecute"中第一个http请求上的数据集将在下一个http请求中可见。如果这些数据恰好被.Net重新分配给另一个线程(在绝大多数情况下都会发生这种情况),那么这些数据在"EndExecute"中就不可用了
  2. Thread.SetData对于我的用例来说问题更大。它需要传入数据槽,如果我要传入Thread.GetNamedDataSlot的返回值中的数据槽,那么该信息在整个应用程序域中都是可用的;因为命名的数据槽在线程之间共享
  3. CallContext.SetData类似于ThreadStatic(这意味着它不被应用程序域共享,但如果不同的http请求被分配到相同的底层OS/.Net线程,它们将看到相同的数据)。CallContext.SetData提供了一种额外的功能,可以封送RPC调用的上下文数据,这与当前所问的问题无关
  4. 然后是ThreadLocal类(.Net 4/.Net 4.5)。它似乎可以解决我问题的一部分,我可以将它传递到BeingExecute操作的stateObject中,并从endExecute操作中的同一stateObject参数中提取。从这个角度来看,ThreadLocal似乎是为.Net的异步支持而编写的。但当我需要像HttpContext.Current那样访问它时,它就不起作用了,因为我看不到保存它的"逻辑线程静态"实例的方法(除非我在前面的3点中说错了什么)
  5. 最后,CallContext.LogicalSetData似乎实现了我想要实现的目标。使用CallContext.LogicalSetData和CallContext.LLogicalGetData方法集,我应该能够实现类似HttpContext.Current的影响,它可以正确地用于"逻辑任务执行">

现在问题来了:

  1. 我上面说的一切都对吗。请更正我提出的任何和所有不正确的主张
  2. 对于.Net中线程静态类型的功能,还有其他我错过的选项吗
  3. CallContext.LogicalSetData/LogicalGetData是否将上下文数据传递给RPC调用(msdn页面没有明确提及,http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext.logicalsetdata(v=vs.110).aspx)
  4. 使用CallContext.LogicalSetData/LogicalGetData是否有任何缺点(性能方面或其他方面)
  5. 此页面介绍了LogicalSetData的写时复制行为:http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html.在异步处理程序/async MVC 5操作方法的上下文中,如果我使用logicalsetdata保存引用类型,然后更改引用类型的状态,会有什么影响。反驳是什么
  6. 对于mutation/localsetdata/async,我仍然看不出对象发生了什么变化。当异步方法启动时,写时复制行为将在下次调用logicalsetdata时触发上下文数据的副本。这是一个浅层副本,所以我的引用对象现在实际上由两个逻辑上下文共享,并且一个上下文中的更改在另一个上下文是可见的,这是我通常对引用类型的期望

这是一个很长的问题,有很多参考资料,但希望我的研究做得很好,答案也会让其他人受益。

我正试图找到一种方法来设置"逻辑线程"特定的数据,这些数据可以在"逻辑"http请求期间一致访问

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

此外,我预计在任何OS/.Net线程上的"BeginExecute"部分中的数据集在随后的http请求中都不可用

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

HttpContext.Current在使用例如async.asmx web服务方法时无法正常工作。

我觉得这很奇怪。我还没有尝试过,但如果您在.NET 4.5上运行,目标是.NET 4.5(即,在web.config中将targetFramework设置为4.5),并且没有使用async void,那么它应该可以工作。

[ThreadStatic]、线程本地数据槽、(非逻辑)CallContextThreadLocal都是线程特定的数据,不适用于异步代码。

我上面所说的一切都正确吗。请更正我提出的任何和所有不正确的主张。

你的问题中确实有太多的文字。堆栈溢出是一个问答;一个网站,而不是辅导网站。

对于.Net中线程静态类型的功能,还有其他我错过的选项吗。

否。

CallContext.LogicalSetData/LogicalGetData是否将上下文数据传递给RPC调用

我不知道。试试看。

使用CallContext.LogicalSetData/LogicalGetData.是否有任何缺点(性能方面或其他方面)

有一个明确的性能打击。.NET框架针对常见情况进行了高度优化(没有逻辑调用上下文数据)。

如果我使用logicalsetdata保存引用类型,然后更改引用类型的状态,会有什么影响。

逻辑CallContext具有写入时浅拷贝行为。因此,任何类型的异步fork/join并发(即Task.WhenAll)最终都将共享该状态。如果你使用ConfigureAwait(false),你也可能会遇到比赛条件。

为了真正解决您的问题,我建议您首先了解HttpContext.Current为什么不能按预期工作;我的猜测(没有看到项目)是targetFramework被设置为4.0而不是4.5。如果你能让HttpContext.Current.Items发挥作用,它是最具性能的选择。

相关内容

  • 没有找到相关文章

最新更新