5

我有一个我编写的日志框架,它能够跟踪“日志上下文”。它有一个可插拔的策略框架,但是我最常使用的是一个 ThreadStatic 变体,它在变量中跟踪上下文[ThreadStatic]。我一直在尝试解决多线程工作流中记录上下文的问题。目标是让共享一个公共线程的所有方法和类的所有调用的所有日志条目记录相同的上下文信息。由于理论上每个线程都应该获得自己的 ThreadStatic 变量,因此这个想法似乎很简单。

public class ThreadStaticLoggingContextStrategy: ILoggingContextStrategy
{
    public ThreadStaticLoggingContextStrategy()
    {
        Debug.WriteLine("[INITIALIZE] A new instance of 'ThreadStaticLoggingContextStrategy' has been created.");
    }

    [ThreadStatic] private LoggingContext _context;

    public LoggingContext GetLoggingContext()
    {
        if (_context == null)
            _context = new LoggingContext();

        return _context;
    }
}

实际上,似乎 ThreadStatic 数据实际上是跨线程共享的。这违背了我对线程的理解。我很难弄清楚问题出在哪里,直到我输入了一个额外的日志条目,该条目跟踪每个线程何时清除线程上下文(所有线程都在主循环上运行......在开始时,如果必要的消息是接收到,上下文被初始化,最后在 finally 子句中,它的重置。)以下日志记录是一致的:

[2011-12-15 16:27:21,233] [调试] [TPI.LTI.Eventing.GroupCreatedNotificationHandler: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 324]: (ContextId=184e82dd-152b-4bb5- a2c6-3e05b2365c04;TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6;HandlerName=GroupCreatedNotificationHandler;HandlerId=WORKDEVELOPMENT.1)将工具“0967e031-398f-437d-8949-2a17fe844df0”的事件推送到 http://tpisondev。 com/tpi/lti/服务/事件...

[2011-12-15 16:27:21,259] [调试] [TPI.LTI.Facades.LTIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 299]: (ContextId=184e82dd-152b-4bb5- a2c6-3e05b2365c04; TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6; HandlerName=GroupCreatedNotificationHandler; HandlerId=WORKDEVELOPMENT.1)获取工具实例 guid 0967e031-398f-437d-8949-2a17fe844df0 的 LTI 工具实例:

[2011-12-15 16:27:21,318] [调试] [TPI.LTI.Facades.LTIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 299]: (ContextId=184e82dd-152b-4bb5- a2c6-3e05b2365c04;TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6;HandlerName=GroupCreatedNotificationHandler;HandlerId=WORKDEVELOPMENT.1)找到工具实例 guid 0967e031-398f-437d-8949-2a17fe844df 的 LTI 工具实例。

[2011-12-15 16:27:21,352] [调试] [TPI.LTI.Facades.TPIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 299]: (ContextId=184e82dd-152b-4bb5- a2c6-3e05b2365c04;TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6;HandlerName=GroupCreatedNotificationHandler;HandlerId=WORKDEVELOPMENT.1)在“http://tpidev.pearsoncmg.com/tpi/lti/service/event”向 TPI 发布事件...

[2011-12-15 16:27:21,428] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.2_Thread: 301]: [LOG] 重置日志记录上下文!!

[2011-12-15 16:27:21,442] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler:TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.2_Thread:299]:队列中没有待处理的消息。GroupCreatedNotificationHandler.WORKDEVELOPMENT.2 处理程序正在等待...

[2011-12-15 16:27:22,282] [DEBUG] [TPI.LTI.Facades.TPIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 301]:事件发布到 TPI。

[2011-12-15 16:27:22,283] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler:TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread:301]:收到来自提供商的回复:

您可以看到在这种特殊情况下有两个线程,1_Thread 和 2_Thread。我用斜体表示应该包含在 1_Thread 的每个日志条目开头的上下文数据。我在 2_Thread 中将日志上下文重置的位置加粗。在那之后,1_Thread 的所有上下文信息都丢失了。到目前为止,在几十个测试中,所有线程的上下文信息在另一个日志上下文被重置后都会丢失。

我误解了ThreadStatic吗?自 2001 年以来,我一直在编写 C# 代码,而我以前从未经历过这种行为。.NET 4 中似乎有一个新ThreadLocal<T>类,但是我不确定它是否只是在内部使用了 ThreadStatic,因此会出现同样的问题,或者它的功能是否不同(希望更可靠。)对此问题的任何见解将不胜感激!谢谢!

4

2 回答 2

15

因为该字段不是 static。它仅适用于静态字段。

如果这是4.0,也许看看ThreadLocal<T>

于 2011-12-15T23:45:57.140 回答
0

正如@MarcGravell 指出的那样,您的领域不是静态的,这是您的问题的原因。

但是,如果消费者要存储GetLoggingContext此值的返回值,则可以在线程之间共享。我通常尝试使用任何ThreadStatic变量作为类的实现细节,而不是将它们暴露在它之外。

于 2011-12-15T23:50:46.943 回答