线程本地存储:Thread-Relative 静态字段和数据槽

可以使用托管线程本地存储(TLS)来存储线程和应用程序域唯一的数据。 .NET 提供了两种方法来使用托管 TLS:线程相对静态字段和数据槽。

  • 如果可以在编译时预测确切需求,请使用线程相对静态字段(Visual Basic 中的线程相对 Shared 字段)。 线程相对静态字段的性能最佳。 它们还支持编译时类型检查。

  • 如果只能在运行时发现实际需求,请使用数据槽。 数据槽使用起来比线程相对静态字段更慢、更加棘手。由于数据存储为类型 Object,因此使用前必须将它强制转换为正确类型。

在非托管 C++ 中,使用 TlsAlloc 动态分配槽,使用 __declspec(thread) 声明应在线程相对存储中分配变量。 线程相关静态字段和数据槽提供此行为的托管模型。

可以使用 System.Threading.ThreadLocal<T> 类来创建在首次使用对象时迟缓初始化的线程本地对象。 有关详细信息,请参阅 延迟初始化

托管 TLS 中的数据的唯一性

无论是使用线程局部静态字段还是数据槽,托管 TLS 中的数据对于线程和应用程序域的组合都是唯一的。

  • 在应用程序域中,一个线程不能修改另一个线程中的数据,即使两个线程都使用相同的字段或槽。

  • 当线程从多个应用程序域访问同一字段或槽时,每个应用程序域中都会保留一个单独的值。

例如,如果一个线程设置了一个线程相关静态字段的值,进入另一个应用程序域,然后检索该字段的值,那么在第二个应用程序域中检索到的值将不同于在第一个应用程序域中的值。 为第二个应用程序域中的字段设置新值不会影响第一个应用程序域中的字段值。

同样,当线程在两个不同的应用程序域中获得相同的命名数据槽时,第一个应用程序域中的数据与第二个应用程序域中的数据无关。

线程相对静态字段

如果知道一段数据始终对线程和应用程序域组合是唯一的,请将该 ThreadStaticAttribute 属性应用于静态字段。 此字段的使用方法与其他任何静态字段一样。 字段中的数据对于使用该数据的每个线程都是唯一的。

相对于线程的静态字段比数据槽提供更好的性能,并且具有编译时类型检查的优势。

请注意,任何类构造函数代码都将在访问字段的第一个上下文中的第一个线程上运行。 在同一应用程序域中的所有其他线程或上下文中,如果字段是引用类型,则字段将初始化为 nullNothing 在 Visual Basic 中),如果字段是值类型,则初始化为默认值。 因此,不应依赖类构造函数来初始化线程相对静态字段。 相反,请避免初始化线程相对静态字段,并假定它们初始化为 nullNothing) 或默认值。

数据槽

.NET 提供与线程和应用程序域组合唯一关联的动态数据槽。 有两种类型的数据槽:命名槽和未命名槽。 它们都是通过 LocalDataStoreSlot 结构实现的。

对于命名和未命名的槽,请使用 Thread.SetDataThread.GetData 方法设置和检索槽中的信息。 这些是静态方法,始终处理当前正在执行的线程的数据。

命名槽很方便,因为可以在需要时通过将它的名称传递给 GetNamedDataSlot 方法来检索槽,无需维护对未命名槽的引用。 然而,如果另一个组件对其线程相关的存储使用相同的名称,并且同一个线程执行了来自您的组件和该组件的代码,那么这两个组件可能会损坏彼此的数据。 (此方案假定这两个组件都在同一应用程序域中运行,并且它们不设计为共享相同的数据。

另请参阅