Является ли использование ThreadStatic в коде платформы.NET вредным пережитком ушедшей эпохи?
[ThreadStatic]
используется в разных местах в.NET Framework для обеспечения окружающего контекста для различных функций (например, Transaction.Current
, который используется для TransactionScope
).
К сожалению, это означает, что функции, которые выполняют некоторое жонглирование потоков (ASP.NET, код асинхронного ключевого слова), переключают потоки, но не копируют TransactionScope
так что особенности как TransactionScope
не работает, как вы могли ожидать.
Есть еще один механизм, CallContext.LogicalGetData
(подробнее здесь), который правильно копирует через состояние во время переключения потоков (по крайней мере, в.NET 4.5). Мне кажется, что TransactionScope
было бы лучше, если бы он использовал это, а не [ThreadStatic]
,
Если функции, которые используют [ThreadStatic]
были написаны сегодня, а не как существующие функции с требованиями обратной совместимости, были бы они написаны с использованием CallContext.(G|S)etLogicalData
?
2 ответа
На самом деле они имеют очень разные варианты использования.
ThreadStatic
не может передать значение черезawait
или аналогичный контекстный переключатель.CallContext
не может сохранить значение для потока
Итак, вы видите, один не может заменить другой. ThreadStatic
это примитив низкого уровня. Я не думаю, что его случаи использования стали меньше после CallContext
и т.д. пришли вместе. Обратите внимание, что его варианты использования крайне малы - я думаю, что в последний раз я использовал его, вероятно, более двух лет назад.
Я бы охарактеризовал такие вещи, как Transaction.Current
как злоупотребление TLS. Он никогда не был предназначен для этого, и поэтому, когда TLS, казалось, нарушил асинхронность, это было только потому, что он никогда не должен был использоваться для этого в первую очередь.
Да. Я уверен, что в то время это казалось хорошей идеей, но [ThreadStatic] уводит разработчиков в ложное чувство безопасности. Поля ThreadStatic имеют много недостатков глобальных переменных, и единственное преимущество (разные потоки имеют свои собственные экземпляры глобальной вещи) имеет соответствующий недостаток (эта вещь исчезает, если вы переключаете потоки). Это не победа. Глобалы отстойные, а нить-статические глобалы отстойны на столько же, сколько и обычные.
Я работаю над довольно большой кодовой базой, несколько лет назад, с десятками разработчиков (возможно, пару сотен, если вы могли бы людей, которые покинули команду за последние несколько лет), и [ThreadStatic] кусает нас на регулярной основе. У нас есть тонны кода, который скрытно использует глобальный поток ThreadStatic, который, конечно, ломается, если вы что-то делаете в рабочем потоке. Как я уже сказал, я уверен, что в то время это казалось хорошей идеей... но сейчас это обошлось нам намного дороже, чем когда-либо покупало.
Альтернативой может быть передача одного и того же объекта (HttpContext, Transaction и т. Д.) В каждый метод, который зависит от него. Это требует больше печатания, но по моему не столь скромному мнению, это все еще лучше в конечном счете.