更新于2014年11月18日——在浏览log4net源代码库时,我发现LogicalThreadContext的实现已于2011年11月进行修改,使用CallContext.LogicalSetData(并使用LogicalGetData获取)存储其属性。这很重要,因为这意味着LogicalThreadContext现在应该能够正常工作。存储在LogicalThreadContext中的任何数据都应该“流动”到任何子线程或任务中。与ThreadContext(和LogicalThreadContext的旧实现)相比,存储在上下文中的数据将仅保留在当前线程中,而不会传递到子线程/任务。
如果您感兴趣,可以查看以下更改:
希望偶然发现这个旧问题的人会发现这些信息很有用。 log4net提供了两种不同的“线程上下文”对象:ThreadContext和LogicalThreadContext,每个对象都有一个属性包,即Properties。ThreadContext具有ThreadContextProperties包,而LogicalThreadContext具有LogicalThreadContextProperties包。ThreadContext可能更常被称为“MDC”。 LogicalContext可能更常被称为“LDC”。 本文中将使用简称。
MDC.Properties使用System.Threading.Thread.SetData实现,而LDC.Properties使用System.Runtime.Remoting.Messaging.CallContext.SetData实现。
相比之下,NLog只公开“MDC”(现在称为MappedDiagnosticContext)以存储线程本地属性。 NLog的实现使用System.Threading.Thread.SetData,因此其实现与log4net的实现相同。
在log4net和NLog中,“MDC”属性都存储在字典中,该字典本身存储在线程本地存储中。
在这种情况下,将字典存储在带有[ThreadStatic]修饰的类成员变量中是否等效?
[ThreadStatic]
private static IDictionary<string, string> threadProperties;
使用.NET 4.0的新ThreadLocal类,有什么等效(或相似)的声明?
最终,LDC和MDC之间的实际差异是什么?即使阅读了上面链接的MSDN主题,我仍然不清楚。你什么情况下会真正使用其中的一个而不是另一个?似乎我看到的大多数关于log4net和context的参考/示例都是GDC(全局-我理解的),NDC(嵌套-我也理解的)和MDC。在谷歌搜索时,我能找到的与LDC(或LogicalThreadContext)相关的大多数参考资料都与提交到log4net源代码存储库有关,而不是真实世界的用法。 LDC几乎从不在问题或示例中提到。
我找到了this链接,它提供了一些来自log4net开发人员Nicko Cadell的很好的信息,但对我来说仍然不清楚。
一个更大的问题,与log4net无直接关系,那就是Thread.SetData和CallContext.SetData之间的实际区别是什么?
根据CallContext MSDN文章,CallContext数据可以传播到另一个AppDomain。为了传播,存储在CallContext中的数据项必须公开ILogicalThreadAffinative接口。所以,这似乎是Thread.SetData和CallContext之间的一个区别。根据Nicko Cadell的链接,log4net没有实现ILogicalThreadAffinative,因此LDC属性将不会被传播。
也许这里有足够的信息让我能回答自己的问题,也可能不行。我仍在努力理解。
如果您使用log4net,您是否使用MDC,LDC或两者都使用?如果您使用MDC,是因为大多数“现实世界”的示例似乎使用它吗?如果您使用LDC,则使用它的特定原因是什么?如果您同时使用两者,您如何选择何时使用哪个?
请注意,我看过一些关于MDC(以及可能是LDC)在ASP.net应用程序中由于线程切换而可能无法正常工作的文章。我对此问题并不特别感兴趣,因为我没有在ASP.net上工作。
实际上,我在SO上找到了几篇有用的帖子,可能有助于讨论: What are best practices for using thread local storage in .NET?
提前致谢!
CallContext
应该可以工作,但是log4net中存在(或曾经存在)一个bug,阻止了LogicalThreadContext
与其良好协同工作。 - Jon Skeet