线程本地存储(Thread Local Storage),字面意思就是专属某个线程的存储空间。变量大体上分为全局变量和局部变量,一个进程中的所有线程共享地址空间,这个地址空间被划分为几个固有的区域,比如堆栈区,全局变量区等,全局变量存储在全局变量区,虚拟地址固定;局部变量存储在堆栈区,虚拟地址不固定。每个线程都有自己的栈空间,局部变量就存储在栈空间里面,虽然这个局部变量是与线程相联系的,但是这个局部变量不能在不同的函数栈中互相直接访问,但TLS可以,概括来讲,TLS是属于线程的“局部变量”,作用域为线程作用域,而不像全局变量为全局作用域,局部变量为局部作用域,因为这个变量独属于这个线程,所以这个变量是线程安全的。
.NET 提供了下面两种托管 TLS 使用方式:线程相对静态字段和数据槽。
如果可以在编译时预测确切需求,请使用线程相对静态字段(Visual Basic 中的线程相对 Shared
字段)。 线程相对静态字段的性能最佳。 它们还支持编译时类型检查。
如果只能在运行时发现实际需求,请使用数据槽。 数据槽使用起来比线程相对静态字段更慢、更加棘手。由于数据存储为类型 Object,因此使用前必须将它强制转换为正确类型。
如果确定一条数据始终对线程和应用域是唯一的,请向静态字段应用 ThreadStaticAttribute 属性。 此字段的使用方法与其他任何静态字段一样。 此字段中的数据对使用它的每个线程都是唯一的。
线程相对静态字段的性能优于数据槽,并支持编译时类型检查。
请注意,任何类构造函数代码都会在访问此字段的首个上下文中的第一个线程上运行。 在同一应用域中的其他所有线程或上下文中,如果字段是引用类型,便会初始化为 null
(Visual Basic 中的 Nothing
);如果字段是值类型,便会初始化为默认值。 因此,不得依赖类构造函数来初始化线程相对静态字段。 相反,请避免初始化线程相对静态字段,而是假设它们初始化为 null
(Nothing
) 或默认值。
.NET 提供了对于线程和应用程序域都是唯一的动态数据槽。 数据槽分为下列两种类型:命名槽和未命名槽。 两种类型都是使用 LocalDataStoreSlot 结构实现。
若要创建命名数据槽,请使用 Thread.AllocateNamedDataSlot 或 Thread.GetNamedDataSlot 方法。 若要获取对现有命名槽的引用,请将它的名称传递给 GetNamedDataSlot 方法。
若要创建未命名数据槽,请使用 Thread.AllocateDataSlot 方法。
对于命名槽和未命名槽,请使用 Thread.SetData 和 Thread.GetData 方法设置和检索槽中的信息。 这些静态方法始终处理当前正在执行它们的线程的数据。
命名槽非常便捷,因为可以在需要时检索槽,具体操作是将它的名称传递给 GetNamedDataSlot 方法,而不用维护对未命名槽的引用。 不过,如果另一个组件对线程相对存储使用相同的名称,并且线程同时执行你的组件和另一个组件的代码,这两个组件可能会相互损坏数据。 (此方案假定这两个组件都在同一个应用域中运行,并不旨在共享相同的数据。)