Почему данные ThreadStatic неожиданно распределяются между потоками? - PullRequest
5 голосов
/ 16 декабря 2011

У меня есть созданная мной среда ведения журналов, которая может отслеживать «контекст ведения журнала».У него есть подключаемая стратегия, однако чаще всего я использую вариант 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] [DEBUG] [TPI.LTI.Eventing.GroupCreatedNotificationHandler: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 324]: (ContextId = 184e82dd-152b-4bb5-a2dd1b1-e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e0e5e5e1e5e5e5e5e1e1e1e1e1e5e0e1e5e1e1e1-d0305-e-5-e-5-db1-3b46dcb4ffd6; HandlerName = GroupCreatedNotificationHandler; HandlerId = WORKDEVELOPMENT.1) Событие отправки для инструмента '0967e031-398f-437d-8949-2a17fe844df0' в 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) Получение экземпляра инструмента LTI для guid экземпляра инструмента 0967e031-398f-437d-8949-2a17fe844df0:

[2011-12-15 16: 27: 21,318] [DEBUG] [TPI,3b46dcb4ffd6; HandlerName = GroupCreatedNotificationHandler; HandlerId = WORKDEVELOPMENT.1) Найден экземпляр инструмента LTI для guid экземпляра инструмента 0967e031-398f-437d-8949-2a17fe844df0.

[21-12,352: 27] [DEBUG] [TPI.LTI.Facades.TPIFacade: TPI.LTI.Provisioning.Handlers.GroupCreatedNotificationHandler.WORKDEVELOPMENT.1_Thread: 299]: (ContextId = 184e82dd-152b-4bb5-a2c0ede1e5c5e05; 053).e8dd-4fa1-9107-3b46dcb4ffd6; HandlerName = GroupCreatedNotificationHandler; HandlerId = WORKDEVELOPMENT.1) Публикация события в TPI в '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] Сброс контекста ведения журнала !!

[2011-12-15 16: 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 года, и никогда раньше не сталкивался с таким поведением. Кажется, в .NET 4 появился новый класс ThreadLocal<T>, однако я не уверен, что он все равно просто использовал ThreadStatic для внутреннего использования, и поэтому у него возникла бы та же проблема, или он функционировал бы по-другому (и, надеюсь, более надежно). Любое понимание в эту проблему будет очень ценным! Спасибо!

Ответы [ 2 ]

15 голосов
/ 16 декабря 2011

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

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

0 голосов
/ 16 декабря 2011

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

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

...