线程 (thread)

Microsoft 专用

thread 扩展存储类修饰符用于声明线程本地变量。 对于 C++11 及更高版本中的可移植等效,请将 thread_local 存储类说明符用于可移植代码。 在 Windows 上,thread_local 是使用 __declspec(thread) 实现的。

语法

__declspec(thread)声明符

注解

线程本地存储 (TLS) 是多线程进程中的每个线程为线程特定的数据分配存储时所采用的机制。 在标准多线程程序中,数据在给定进程的所有线程间共享,而线程本地存储是用于分配每个线程数据的机制。 有关线程的完整讨论,请参阅多线程

线程本地变量的声明必须将扩展的特性语法__declspec 关键字和 thread 关键字一起使用。 例如,以下代码声明了一个整数线程局部变量,并用一个值对其进行初始化:

__declspec( thread ) int tls_i = 1;

在动态加载的库中使用线程局部变量时,需注意可能导致线程局部变量无法正确初始化的因素:

  1. 如果变量是通过函数调用(包括构造函数)初始化的,则只会为导致二进制文件/DLL 加载到进程中的线程,以及在加载二进制文件/DLL 之后启动的线程调用此函数。 对于加载 DLL 时已经在运行的其他任何线程,不会调用初始化函数。 动态初始化在 DLL_THREAD_ATTACH 的 DllMain 调用时进行,但如果线程启动时 DLL 不在进程中,DLL 永远不会收到该消息。

  2. 使用常量值静态初始化的线程局部变量通常在所有线程上正确初始化。 但截至 2017 年 12 月,Microsoft C++ 编译器有一个已知的一致性问题,即 constexpr 变量接收动态而不是静态初始化。

    注意:预计这两个问题都将在编译器的未来更新中得到修复。

另外,在声明线程本地对象和变量时必须遵守下列准则:

  • 可以只将 thread 特性应用于类和数据声明和定义;thread 不能用于函数声明或定义。

  • 只能在具有静态存储持续时间的数据项上指定 thread 特性。 这包括全局数据对象(staticextern)、本地静态对象和类的静态数据成员。 不能声明带 thread 特性的自动数据对象。

  • 必须为线程本地对象的声明和定义使用 thread 特性,无论声明和定义是在同一文件中还是单独的文件中发生。

  • 无法将 thread 特性用作类型修饰符。

  • 由于允许使用 thread 特性的对象的声明,因此下面的两个示例在语义上是等效的:

    // declspec_thread_2.cpp
    // compile with: /LD
    __declspec( thread ) class B {
    public:
       int data;
    } BObject;   // BObject declared thread local.
    
    class B2 {
    public:
       int data;
    };
    __declspec( thread ) B2 BObject2;   // BObject2 declared thread local.
    
  • 标准 C 允许使用涉及引用自身的表达式初始化对象或变量,但只适用于非静态对象。 虽然 C++ 通常允许使用涉及引用自身的表达式动态初始化对象,但是不允许将这种类型的初始化用于线程本地对象。 例如:

    // declspec_thread_3.cpp
    // compile with: /LD
    #define Thread __declspec( thread )
    int j = j;   // Okay in C++; C error
    Thread int tls_i = sizeof( tls_i );   // Okay in C and C++
    

    包含将初始化的对象的 sizeof 表达式不构成对自身的引用,允许在 C 和 C++ 中使用该表达式。

结束 Microsoft 专用

另请参阅

__declspec
关键字
线程本地存储 (TLS)