线程局部存储

**线程局部存储**（，縮寫：**TLS**）是一种存储持续期（storage duration），对象的存储是在线程开始时分配，线程结束时回收，每个线程有该对象自己的实例。这种对象的链接性（linkage）可以是静态的也可是外部的。

TLS的一个例子是用全局变量``表示错误号。这可能在多线程并发时产生同步错误。线程局部存储的`[[errno]]`是个解决办法。

## Windows的实现
每个进程都有一组标志，共TLS_MINIMUM_AVAILABLE(==64)个。每个标志可以被设为FREE或INUSE，表示该TLS元素是否正在使用。注意这组标志属进程所有。当系统创建一个线程的时候，会为该线程分配与线程关联的、属于线程自己的PVOID型数组（共有TLS_MINIMUM_AVAILBALE个元素），数组中的每个PVOID可以保存任意值。

Windows API函数`TlsAlloc`用于获取进程中一个未用的*TLS slot index*。然后将该标志从FREE改为INUSE，并返回该标志在位数组中的索引，通常将该索引保存在一个全局变量中，因为这个值会在整个进程范围内（而不是线程范围内）使用。

调用TlsSetValue(dwTlsIndex,pvTlsValue)将一个PVOID值放到线程的数组中dwTlsIndex指定的具体位置。

函数`TlsGetValue`与`TlsSetValue`用于通过*TLS slot index*读写一个线程局部存储变量所指向的内存块。函数`TlsFree`用于释放*TLS slot index*。

在的FS:[0x2C]地址处，存放的是线程局部存储表的地址。每个线程用它自己的线程局部存储表的拷贝。TlsAlloc返回表中一个未使用的索引。因此每个线程可以用TlsSetValue(index)设置线程局部存储值，用TlsGetValue(index)获取线程局部存储值。

Windows可执行程序也可以定义一个节（section），映射到进程每个线程的不同的内存分页。这种节只定义在主程序里，动态链接库（DLL）不应该包含这种节因为不会被LoadLibrary函数在加载时初始化。

对于Windows系统来说，全局变量或静态变量会被放到".data"或".bss"段中，但当使用__declspec(thread)定义一个线程私有变量的时候，编译器会把这些变量放到PE文件的".tls"段中。当系统启动一个新的线程时，它会从进程的堆中分配一块足够大小的空间，然后把".tls"段中的内容复制到这块空间中，于是每个线程都有自己独立的一个".tls"副本。所以对于用__declspec(thread)定义的同一个变量，它们在不同线程中的地址都是不一样的。对于一个TLS变量来说，它有可能是一个C++的全局对象，那么每个线程在启动时不仅仅是复制".tls"的内容那么简单，还需要把这些TLS对象初始化，必须逐个地调用它们的全局构造函数，而且当线程退出时，还要逐个地将它们析构，正如普通的全局对象在进程启动和退出时都要构造、析构一样。Windows PE文件的结构中有个叫数据目录的结构。它总共有16个元素，其中有一元素下标为IMAGE_DIRECT_ENTRY_TLS，这个元素中保存的地址和长度就是TLS表（IMAGE_TLS_DIRECTORY结构）的地址和长度。TLS表中保存了所有TLS变量的构造函数和析构函数的地址，Windows系统就是根据TLS表中的内容，在每次线程启动或退出时对TLS变量进行构造和析构。TLS表本身往往位于PE文件的".rdata"段中。

## Pthreads的实现
Pthreads API定义了线程特定的数据。

函数`pthread_key_create`与`pthread_key_delete`创建与删除一个键，用于线程特定的数据。键的类型被称为`pthread_key_t`。键可以被所有线程看到。在每个线程，键可以用`pthread_setspecific`函数关联到线程特定的数据。数据可以随后用`pthread_getspecific`函数获取。
## 特定于语言的实现

### C and C++
C11的关键字`_Thread_local`用于定义线程局部变量。在头文件`<threads.h>`定义了`thread_local`为上述关键词的同义。例如：


```c
#include <threads.h>
thread_local int foo = 0;
```


C++11引入了`thread_local`关键字用于下述情形： 
*命名空间(全局)变量
*文件静态变量
*函数静态变量
*静态成员变量

此外，不同编译器提供了各自的方法声明线程局部变量：
*Solaris Studio C/C++, IBM XL C/C++, GNU C, Clang与Intel C++ Compiler (Linux平台)使用语法： 
*: `__thread int number;`
*Visual C++, Intel C/C++ (Windows systems), C++Builder, 与Digital Mars C++ 使用语法：
*: `__declspec(thread) int number;`
*C++Builder也可以使用语法： 
*: `int __thread number;`

Windows的版本早于Vista与Server 2008, `__declspec(thread)`对于DLL只用于DLL被可执行程序绑定静态加载，在*LoadLibrary()*函数动态加载DLL将报告protection fault或data corruption。

### Java
Java语言中，线程局部变量使用类对象表示。ThreadLocal保持了变量的类型T，可以通过get/set方法访问。例如，ThreadLocal保持了Integer值：

```java
private static final ThreadLocal<Integer> myThreadLocalInteger = new ThreadLocal<Integer>();
```


Oracle/OpenJDK使用操作系统线程以避免性能代价。

### .NET 语言: C# 与Visual Basic.Net
.NET Framework语言，静态域可标记[ThreadStatic attribute](http://msdn2.microsoft.com/en-us/library/system.threadstaticattribute(vs.80).aspx) :


```csharp
class FooBar {
   [ThreadStatic] static int foo;
}
```


.NET 4.0，System.Threading.ThreadLocal<T> 可用于分配与惰性装入线程局部变量。

```csharp
class FooBar {
   private static System.Threading.ThreadLocal<int> foo;
}
```


Also [an API](http://msdn2.microsoft.com/en-us/library/system.threading.thread.getnameddataslot(vs.80).aspx)
is available for dynamically allocating thread-local variables.

### Python
Python语言从版本2.4开始，*threading*模块的*local*类可用于创建线程局部存储：

```python
import threading
mydata = threading.local()
mydata.x = 1
```


### Ruby
Ruby语言能创建/访问线程局部变量使用[]=/[]方法:


```ruby
Thread.current[:user_id] = 1
```


## 参考文献

## 外部链接
*ELF Handling For Thread-Local Storage  &mdash; Document about an implementation in C or C++.
*ACE_TSS< TYPE > Class Template Reference
*RWTThreadLocal<Type> Class Template Documentation
*Article "Use thread-local Storage to Pass Thread Specific Data " by Doug Doedens
*"Thread-Local Storage " by Lawrence Crowl
*Article "It's Not Always Nice To Share " by Walter Bright
*Practical ThreadLocal usage in Java: https://web.archive.org/web/20161220151503/https://www.captechconsulting.com/blogs/a-persistence-pattern-using-threadlocal-and-ejb-interceptors
*GCC "https://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Thread-Local.html "