5

请考虑这些场景:

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

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

此外,如果第二个请求被分配了先前分配给第一个 http 请求的线程它在“BeginExecute”部分,但随着第一个 http 请求进入其异步操作(并且它可能仍在完成其异步操作),该线程被释放。

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

此外,我想将此数据公开为 HttpContext.Current 类型的静态属性。对于某些人来说,这可能会让人感到意外,但是如果您使用例如异步 .asmx Web 服务方法,HttpContext.Current 将无法正常工作。我确定我已经阅读了网络上的内容,其中说 HttpContext.Current 应该始终返回正确的 HttpContext,但我在 .asmx web-methods 的 EndExecuteMethod 中看到它为空。如果 somecan 可以确认我的最后一个陈述是否正确,那就太好了,但这个陈述并不是我想在这里提出的总体问题。

在阅读了大量文献后(例如log4net.ThreadContext 和 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 文档),这是我的推论:

  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.LogicalGetData 方法,我应该能够实现类似 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。在异步处理程序/异步 MVC 5 操作方法的上下文中,如果我使用logicalsetdata 保存引用类型并稍后更改引用类型的状态会有什么影响。什么是recucursions。
  6. 对于mutation/logicalsetdata/async,我仍然无法通过对对象进行变异来查看问题所在。当异步方法启动时,写时复制行为将在下次调用logicalsetdata时触发上下文数据的副本。这是一个浅拷贝,所以我的引用对象现在实际上由 2 个逻辑上下文共享,并且一个上下文中的更改在另一个上下文中是可见的,这是我通常期望的引用类型。

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

4

1 回答 1

0

我试图找出一种方法来设置可以在“逻辑”http请求期间一致访问的“逻辑线程”特定数据

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

此外,我希望它所在的任何 OS/.Net 线程上的“BeginExecute”部分中的数据集在后续的 http 请求中都不可用

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

如果您使用例如异步 .asmx Web 服务方法,HttpContext.Current 将无法正常工作。

我觉得这很令人惊讶。我还没有尝试过,但它应该可以工作 -如果您在 .NET 4.5 上运行,以 .NET 4.5 为目标(即在您的.NET 中targetFramework设置为),并且没有使用.4.5web.configasync void

[ThreadStatic], 线程本地数据槽, (non-logical) CallContext, 和ThreadLocal都是线程特定的数据,不适用于异步代码。

我上面所说的一切是否正确。请更正我所做的任何和所有不正确的声明。

您的问题中的文字确实太多了。Stack Overflow 是一个问答网站,而不是指导网站。

我错过了.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如果您可以使其正常工作,是性能最高的选择。

于 2014-05-26T03:08:12.767 回答