Локальное хранилище потока: статические поля потока и области данных
Вы можете использовать управляемое локальное по отношению к потоку хранилище (TLS) для хранения данных, которые являются уникальными для потока и домена приложения. Платформа .NET предоставляет два способа работы с TLS: статические поля потоков и ячейки данных.
Статические поля потоков (поля потоков
Shared
в Visual Basic) можно применять, если вы можете точно прогнозировать потребности во время компиляции. Статические поля потоков обеспечивают более высокую производительность. Они также позволяют выполнять проверки типов во время компиляции.Если фактические требования могут определяться только во время выполнения, используйте области данных. Области данных работают медленнее и менее удобны в использовании, чем статические поля потоков. Данные в них хранятся в качестве типа Object, поэтому перед использованием необходимо привести их к правильному типу.
В неуправляемом коде C++ используются TlsAlloc
для динамического выделения областей данных и __declspec(thread)
для объявления переменных, которые нужно выделять в хранилище потока. Статические поля потоков и области данных реализуют такое же поведение для управляемого кода.
Вы можете использовать класс System.Threading.ThreadLocal<T> для создания локальных по отношению к потоку объектов с отложенной инициализацией (при первом использовании объекта). Дополнительные сведения см. в статье Отложенная инициализация.
Уникальность данных в управляемой локальной памяти потока
Независимо от того, что вы используете — статические поля потоков или области данных — данные в управляемой локальной памяти потока являются уникальными для определенной комбинации потока и домена приложения.
Любой поток в домене приложения не может изменять данные другого потока, даже если оба потока используют одинаковые поля или области памяти.
Если поток обращается к одному полю или области через разные домены приложений, в каждом домене приложения для него хранится отдельное значение.
Таким образом, если поток устанавливает значение для статического поля потока, затем переходит в другой домен приложения и извлекает значение этого поля, он получит во втором домене приложения другое значение, отличное от значения в первом домене приложения. Сохранение нового значения для поля во втором домене приложения никак не влияет на значение этого поля в первом домене приложения.
Аналогичным образом, если поток получает одинаковую именованную область данных в двух разных доменах приложений, данные в первом домене не будут зависеть от данных во втором домене приложения.
Статические поля потоков
Если вы твердо уверены, что некоторые данные всегда уникальны для потока и домена приложения, примените для статического поля атрибут ThreadStaticAttribute. Используйте это поле так же, как любое обычное статическое поле. Данные в этом поле всегда будут уникальными для каждого потока, который его использует.
Статические поля потоков обеспечивают более высокую производительность, чем области данных, и позволяют выполнять проверку типов во время компиляции.
Имейте в виду, что любой код конструктора класса будет выполняться в первом потоке в первом контексте, где происходит обращение к полю. Во всех других потоках и (или) контекстах в том же домене приложения эти поля получают значения null
(Nothing
в Visual Basic), а если они являются ссылочными типами, — значения по умолчанию для соответствующих типов. Таким образом, вы не можете инициализировать статические поля потоков через конструктор класса. Старайтесь не применять инициализацию для статических полей потоков, и всегда предполагайте, что они имеют значение null
(Nothing
) или значение по умолчанию для типа.
Области данных
.NET предоставляет динамически выделяемые ячейки данных, которые являются уникальными для каждой комбинации потока и домена приложения. Есть два типа областей данных: именованные и безымянные. Оба типа реализуются с помощью структуры LocalDataStoreSlot.
Чтобы создать именованную область данных, используйте метод Thread.AllocateNamedDataSlot или Thread.GetNamedDataSlot. Чтобы получить ссылку на существующую именованную область, передайте ее имя в метод GetNamedDataSlot.
Чтобы создать неименованную область данных, используйте метод Thread.AllocateDataSlot.
Как для именованных, так и для неименованных областей данных можно применять методы Thread.SetData и Thread.GetData для сохранения и получения данных. Это статические методы, которые всегда работают с данными того потока, который их выполняет.
Именованные области особенно удобны, так как позволяют в любой момент получить нужную область, передав ее имя в метод GetNamedDataSlot, вместо того чтобы хранить ссылку на безымянную область. Но если другой компонент использует то же имя в своем хранилище потока, при выполнении в одном потоке кода из обоих этих компонентов они могут повредить данные друг друга. (Здесь предполагается, что оба компонента работают в одном домене приложения и не рассчитаны на совместное использование данных.)