c# - 为什么线程之间意外共享 ThreadStatic 数据?

标签 c# multithreading c#-4.0 thread-safety threadstatic

我有一个我编写的日志记录框架,它能够跟踪“日志记录上下文”。它有一个可插入的策略框架,但我最常使用的是一个 ThreadStatic 变体,它在 [ThreadStatic] 中跟踪上下文。多变的。我一直在尝试解决多线程工作流中的日志记录上下文问题。目标是让共享公共(public)线程的所有方法和类的所有调用的所有日志条目记录相同的上下文信息。由于理论上每个线程都应该获得自己的 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] [DEBUG] [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) Pushing event for tool '0967e031-398f-437d-8949-2a17fe844df0' to http://tpidev.pearsoncmg.com/tpi/lti/service/event...

[2011-12-15 16:27:21,259] [DEBUG] [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) Getting LTI Tool Instance for tool instance guid 0967e031-398f-437d-8949-2a17fe844df0:

[2011-12-15 16:27:21,318] [DEBUG] [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) Found LTI Tool Instance for tool instance guid 0967e031-398f-437d-8949-2a17fe844df0.

[2011-12-15 16:27:21,352] [DEBUG] [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) Publishing event to TPI at 'http://tpidev.pearsoncmg.com/tpi/lti/service/event'...

[2011-12-15 16:27:21,428] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.2_Thread: 301]: [LOG] Resetting logging Context!!

[2011-12-15 16:27:21,442] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.2_Thread: 299]: No message pending on queue. GroupCreatedNotificationHandler.WORKDEVELOPMENT.2 handler is waiting...

[2011-12-15 16:27:22,282] [DEBUG] [TPI.LTI.Facades.TPIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 301]: Event published to TPI.

[2011-12-15 16:27:22,283] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 301]: Received a response from provider:

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

我是不是误解了 ThreadStatic?我从 2001 年开始编写 C# 代码,之前从未遇到过这种行为。似乎有一个新的 ThreadLocal<T> .NET 4 中的类,但是我不确定它是否只是在内部简单地使用了 ThreadStatic,因此是否会出现同样的问题,或者它的功能是否不同(希望更可靠。)任何对此问题的洞察力都将不胜感激!谢谢!

最佳答案

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

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

关于c# - 为什么线程之间意外共享 ThreadStatic 数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8528132/

相关文章:

C# 移动折线

c# - WF4 : Workflow stay locked

c#-4.0 - 在 XML 功能区中以编程方式访问功能区控件

c# - C# 属性声明的键盘快捷方式?

c# - 序列化枚举并添加描述

javascript - JavaScript 中的竞争条件与复合赋值

c# - Registry.LocalMachine 和 RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default) 之间有什么区别

c# - 从 C# 调用 ActionScript 3 函数

c# - 比较两个相同结构的任意 JToken-s

c++ - std::thread 不退出