Почему данные 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
переменные как детали реализации класса, вместо того, чтобы выставлять их вне его.