Поделиться через


Локальное хранилище потока: статические поля потока и области данных

Вы можете использовать управляемое локальное по отношению к потоку хранилище (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, вместо того чтобы хранить ссылку на безымянную область. Но если другой компонент использует то же имя в своем хранилище потока, при выполнении в одном потоке кода из обоих этих компонентов они могут повредить данные друг друга. (Здесь предполагается, что оба компонента работают в одном домене приложения и не рассчитаны на совместное использование данных.)

См. также