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

时间:2014-05-15 21:24:08

标签: c# asp.net multithreading asynchronous threadstatic

请考虑这些情景:

  1. 异步.ashx处理程序
  2. 异步.asmx网络服务方法
  3. 同步MVC 5控制器操作方法
  4. 我试图找出一种方法来设置"逻辑线程"在"逻辑"期间可以一致访问的特定数据http请求,即如果数据是在" BeginExecute"中的线程上设置的。您将考虑的部分异步处理程序,该数据在" EndExecute" asnc处理程序的一部分,即使ASP.NET执行" EndExecute"部分在不同的OS / .Net线程上。

    此外,我期待在" BeginExecute"中设置数据。如果第二个请求被分配了先前分配给第一个http请求的线程,那么当它在#34; BeginExecute"部分但是当第一个http请求进入异步操作时,该线程被释放(并且它可能仍在完成其异步操作)。

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

    此外,我想将此数据公开为HttpContext.Current类型的静态属性。对于某些人来说,这可能会让人感到惊讶,但如果您使用的是异步.asmx Web服务方法,则HttpContext.Current无法正常工作。我确信我已经阅读了网上的内容,其中说HttpContext.Current应该总是返回正确的HttpContext,但我在.asmx web-methods的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文档),以下是我的推论:

    1. ThreadStatic是底层OS / .Net线程的本地,而不是" logical"操作,因此在我的例子中;第一个http请求中的数据设置在" BeginExecute"如果第二个http请求被分配了与" BeginExecute"相同的线程,则在下一个http请求中将可见第一个主题。并且这些数据不会在" EndExecute"如果碰巧被.Net重新分配给另一个线程(在绝大多数情况下会发生这种情况)。
    2. Thread.SetData对我的用例来说更成问题。它需要传入数据槽,如果我从Thread.GetNamedDataSlot的返回值传入数据槽,则该信息可在app域中获得;由于命名数据​​槽在线程之间共享。
    3. CallContext.SetData类似于ThreadStatic(这意味着它不会被app域共享,但是如果它们被分配到相同的底层OS / .Net线程,则不同的http请求会看到相同的数据)。 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这样的影响,它可以正确地执行"逻辑任务执行"。
    6. 现在来问题:

      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保存引用类型并稍后更改引用类型的状态会产生什么影响。什么是复制。
      6. 对于mutation / logicalsetdata / async,我仍然无法通过改变对象来查看问题所在。当异步方法启动时,写入时复制行为将在下次调用logicalsetdata时触发上下文数据的副本。这是一个浅层副本,所以我的引用对象现在实际上由2个逻辑上下文共享,并且一个上下文中的更改在另一个上下文中是可见的,这是我通常期望的引用类型。
      7. 一个很长的问题,有很多参考文献,但希望我的研究很好,答案也会让其他人受益。

1 个答案:

答案 0 :(得分:0)

  

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

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

  

此外,我希望“BeginExecute”部分中的数据在任何OS / .Net线程上设置在后续的http请求中

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

  

如果您使用的是异步.asmx网络服务方法,则HttpContext.Current无法正常工作。

我觉得这很令人惊讶。我没有尝试过,但它应该可行 - 如果你在.NET 4.5上运行,目标是.NET 4.5(即,targetFramework设置为4.5 web.config),并且未使用async void

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

  

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

你的问题中有太多的文字。 Stack Overflow是一个Q& A网站,而不是指导网站。

  

我错过了.Net中的线程静态类型功能是否还有其他选项。

没有

  

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

我不知道。试试吧,看看。

  

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

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

  

如果我使用logicalsetdata保存引用类型并稍后更改引用类型的状态会产生什么影响。

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

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