Почему данные ThreadStatic неожиданно распределяются между потоками?

У меня есть среда ведения журналов, которую я написал, которая имеет возможность отслеживать "контекст ведения журнала". У него есть подключаемая стратегия, однако чаще всего я использую вариант 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 его сброс.) Следующее ведение журнала является CONSISTENT:

[2011-12-15 16: 27: 21,233] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 324]: (ContextId = 184e82b5255555 a2c6-3e05b2365c04; TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6; HandlerName=GroupCreatedNotificationHandler; HandlerId=WORKDEVELOPMENT.1) Событие проталкивания для инструмента '0967e031-398f-431 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-4b5 a2c6-3e05b2365c04; TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6; HandlerName=GroupCreatedNotificationHandler; HandlerId=WORKDEVELOPMENT.1) Получение экземпляра инструмента LTI для guid экземпляра инструмента 0967e0317688-689

[2011-12-15 16: 27: 21,318] [DEBUG] [TPI.LTI.Facades.LTIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 299]: (ContextId = 184e82dd-152b-4b5 a2c6-3e05b2365c04; TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6; HandlerName=GroupCreatedNotificationHandler; HandlerId=WORKDEVELOPMENT.1) Обнаружен экземпляр инструмента LTI для экземпляра инструмента guid 0967e031-688-ru

[2011-12-15 16: 27: 21,352] [DEBUG] [TPI.LTI.Facades.TPIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 299]: (ContextId = 184e82dd-152b-4b5-a2c6-3e05b2365c04; TransactionId=1a11130e-e8dd-4fa1-9107-3b46dcb4ffd6; HandlerName=GroupCreatedNotificationHandler; HandlerId=WORKDEVELOPMENT.1) Публикация события для TPI по адресу http: //tpidev.pearsonlti...

[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? Я пишу код на C# с 2001 года и никогда раньше не сталкивался с таким поведением. Кажется, есть новый ThreadLocal<T> класс в.NET 4, однако я не уверен, если бы он просто использовал ThreadStatic для внутреннего использования в любом случае и имел бы для этого ту же проблему, или если он функционировал бы по-другому (и, надеюсь, более надежно). Любое понимание этой проблемы будет БОЛЬШЕ ЦЕННО! Спасибо!

2 ответа

Решение

Потому что это поле не статично. Это относится только к статическим полям.

Если это 4.0, возможно, посмотрите на ThreadLocal<T>

Как указывает @MarcGravell, ваше поле не является статичным, что является причиной вашей проблемы.

Тем не менее, если потребитель будет хранить возврат от GetLoggingContext это значение будет доступно для потоков. Я вообще пытаюсь использовать любой ThreadStatic переменные как детали реализации класса, вместо того, чтобы выставлять их вне его.

Другие вопросы по тегам