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


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

Вы можете использовать локальное хранилище управляемого потока (TLS) для хранения данных, уникальных для домена потока и приложения. .NET предоставляет два способа использования управляемых TLS: статических полей и слотов данных относительно потоков.

  • Используйте потокозависимые статические поля (потокозависимые Shared поля в Visual Basic), если вы можете определить точные потребности на этапе компиляции. Статические поля, относящиеся к потоку, обеспечивают лучшую производительность. Они также предоставляют преимущества проверки типов на этапе компиляции.

  • Используйте слоты данных, если фактические требования могут быть обнаружены только на этапе выполнения. Слоты данных медленнее и более неловко использовать, чем статические поля относительно потока, и данные хранятся в виде типа Object, поэтому перед его использованием необходимо привести его к правильному типу.

В неуправляемом языке C++используется TlsAlloc для динамического выделения слотов и __declspec(thread) объявления, что переменная должна быть выделена в хранилище, относящееся к потоку. Статические поля и слоты данных, относящиеся к потоку, предоставляют управляемую версию этого поведения.

Вы можете использовать класс System.Threading.ThreadLocal<T> для создания локальных для потока объектов, которые инициализируются лениво при первом использовании. Дополнительные сведения см. в ленивой инициализации.

Уникальность данных в управляемом TLS

Независимо от того, используются ли статические поля потока или слоты данных, данные в управляемом TLS уникальны для сочетания домена потоков и приложений.

  • В домене приложения один поток не может изменять данные из другого потока, даже если оба потока используют одно и то же поле или слот.

  • Когда поток обращается к одному полю или слоту из нескольких доменов приложений, отдельное значение сохраняется в каждом домене приложения.

Например, если поток устанавливает значение статического поля, связанного с потоком, входит в другой домен приложения, а затем извлекает значение этого поля, то значение, полученное во втором домене приложения, будет отличаться от значения в первом домене приложения. Задание нового значения для поля во втором домене приложения не влияет на значение поля в первом домене приложения.

Аналогичным образом, когда поток получает тот же именованный слот данных в двух разных доменах приложения, данные в первом домене приложения остаются независимыми от данных во втором домене приложения.

статические поля Thread-Relative

Если вы знаете, что часть данных всегда уникальна для сочетания потоков и домена приложения, примените ThreadStaticAttribute атрибут к статическому полю. Используйте поле, как и любое другое статическое поле. Данные в поле уникальны для каждого потока, использующего его.

Относительные к потоку статические поля обеспечивают лучшую производительность по сравнению с слотами данных и обеспечивают преимущество проверки типов на уровне компиляции.

Помните, что любой код конструктора классов будет выполняться в первом потоке в первом контексте, который обращается к полю. Во всех других потоках или контекстах в одном домене приложения поля будут инициализированы null (Nothing в Visual Basic), если они являются ссылочными типами или значениями по умолчанию, если они являются типами значений. Поэтому не следует полагаться на конструкторы классов для инициализации статических полей относительно потока. Вместо этого избегайте инициализации статических полей относительно потока и предположите, что они инициализированы до null (Nothing) или до значений по умолчанию.

Слоты данных

.NET предоставляет динамические слоты данных, уникальные для сочетания домена потоков и приложений. Существует два типа слотов данных: именованные слоты и неназванные слоты. Оба реализуются с помощью LocalDataStoreSlot структуры.

Для именованных и неименованных слотов используйте методы Thread.SetData и Thread.GetData, чтобы указать и получить информацию в слоте. Это статические методы, которые всегда работают с данными потока, выполняющего их в настоящий момент.

Именованные слоты могут быть удобными, так как вы можете получить слот, когда он понадобится, передав его имя методу GetNamedDataSlot, вместо сохранения ссылки на неименованный слот. Однако если другой компонент использует то же имя для своего потокозависимого хранилища и поток выполняет код как из вашего компонента, так и из другого компонента, оба компонента могут повредить данные друг друга. (В этом сценарии предполагается, что оба компонента выполняются в одном домене приложения, и что они не предназначены для совместного использования одних и тех же данных.)

См. также