`

线程本地存储 (TLS)

 
阅读更多

线程本地存储 (TLS) 是一个方法,通过该方法,给定的多线程进程中的每个线程都可以分配存储线程特定数据的位置。通过 TLS API(TlsAlloc、TlsGetValue、TlsSetValue、TlsFree)方式支持动态绑定(运行时)的线程特定数据。除了现有的 API 实现,Win32 和 Visual C++ 编译器现在还支持静态绑定(加载时间)基于线程的数据。

TLS 的 API 实现

通过 Win32 API 层和编译器实现“线程本地存储”。有关详细信息,请参见 Win32 API 文档中的 TlsAlloc、TlsGetValue、TlsSetValue 和 TlsFree。

Visual C++ 编译器包括使线程本地存储操作更加自动化的关键字,而不是通过 API 层。将在下一节(TLS 的编译器实现)描述此语法。

TLS 的编译器实现

为了支持 TLS,已将新属性 thread 添加到了 C 和 C++ 语言,并由 Visual C++ 编译器支持。此属性是一个扩展存储类修饰符,如上一节中所述。使用 __declspec 关键字声明 thread 变量。例如,以下代码声明了一个整数线程局部变量,并用一个值对其进行初始化:

__declspec( thread ) int tls_i = 1;

TLS 的规则和限制

<!--NONSCROLLING BANNER END-->
<!-- Topic Status -->

声明静态绑定线程的本地对象和变量时必须遵守下列原则:

  • thread 属性只能应用于数据声明和定义。它不能用于函数声明或定义。例如,以下代码将生成一个编译器错误:
    #define Thread  __declspec( thread )
    Thread void func();     // This will generate an error.
  • 只能在具有 static 作用域的数据项上指定 thread 修饰符。包括全局数据对象(包括 staticextern)、本地静态对象和 C++ 类的静态数据成员。不可以用 thread 属性声明自动数据对象。以下代码将生成编译器错误:
    #define Thread  __declspec( thread )
    void func1()
    {
        Thread int tls_i;            // This will generate an error.
    }
    
    int func2( Thread int tls_i )    // This will generate an error.
    {
        return tls_i;
    }
  • 线程本地对象的声明和定义必须全都指定 thread 属性。例如,以下代码将生成错误:
    #define Thread  __declspec( thread )
    extern int tls_i;        // This will generate an error, since the
    int Thread tls_i;        // declaration and definition differ.
  • thread 属性不能用作类型修饰符。例如,以下代码将生成一个编译器错误:
    char __declspec( thread ) *ch;        // Error
  • C++ 类不能使用 thread 属性。但是,可以使用 thread 属性将 C++ 类对象实例化。例如,以下代码将生成一个编译器错误:
    #define Thread  __declspec( thread )
    class Thread C       // Error: classes cannot be declared Thread.
    {
    // Code
    };
    C CObject;

    因为允许使用 thread 属性的 C++ 对象的声明,因此下面两个示例在语义上是等效的:

    #define Thread  __declspec( thread )
    Thread class B
    {
    // Code
    } BObject;               // OK--BObject is declared thread local.
    
    class B
    {
    // Code
    };
    Thread B BObject;        // OK--BObject is declared thread local.
  • 不将线程本地对象的地址视为常数,并且涉及此类地址的任何表达式都不视为常数。在标准 C 中,这种作法的效果是禁止将线程本地变量的地址用作对象或指针的初始值设定项。例如,C 编译器将以下代码标记为错误:
    #define Thread  __declspec( thread )
    Thread int tls_i;
    int *p = &tls_i;       //This will generate an error in C.

    但是,此限制不适用于 C++。因为 C++ 允许动态初始化所有对象,因此可以用使用线程本地变量地址的表达式初始化对象。实现此操作的方式与实现线程本地对象结构的方式相同。例如,以上显示的代码在作为 C++ 源文件编译时不会生成错误。请注意:只有在其中获取地址的线程仍然存在的情况下,线程本地变量的地址才有效。

  • 标准 C 允许使用涉及引用自身的表达式初始化对象或变量,但只适用于非静态作用域的对象。虽然 C++ 通常允许使用涉及引用自身的表达式动态初始化对象,但是这种类型的初始化不允许用于线程本地对象。例如:
    #define Thread  __declspec( thread )
    Thread int tls_i = tls_i;                // Error in C and C++ 
    int j = j;                               // OK in C++, error in C
    Thread int tls_i = sizeof( tls_i )       // Legal in C and C++

    请注意:包含正在初始化的对象的 sizeof 表达式不建立对自身的引用且在 C 和 C++ 中都是合法的。

    C++ 不允许此类对线程数据的动态初始化,因为将来可能要对线程本地存储功能进行增强。

  • 如果 DLL 将任何非本地数据或对象声明为 __declspec(线程),动态加载该 DLL 时会导致保护错误。使用 LoadLibrary 加载所有 DLL 后,每当代码引用非本地 __declspec(线程)数据时,将导致系统故障。由于线程的全局变量空间是在运行时分配的,因此此空间的大小是以应用程序的需求和所有静态链接的 DLL 的需求相加为基础计算出来的。使用 LoadLibrary 时,无法扩展此空间以允许放置用 __declspec(线程)声明的线程本地变量。如果 DLL 可能是用 LoadLibrary 加载的,请在 DLL 中使用 TLS API(如 TlsAlloc)来分配 TLS。
分享到:
评论

相关推荐

    tls线程本地存储例子

    http://blog.csdn.net/infoworld/article/details/49715355 mingw编译

    [并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)调用复制文件接口的案例]

    http://blog.csdn.net/infoworld/article/details/49715355 项目代码,mingw编译.

    Swift中类型安全的线程本地存储-Swift开发

    线程本地存储(TLS)使您可以定义一个变量,每个变量Threadly是一个Swift µframework,可进行类型安全的线程本地存储。 什么是线程本地存储? 线程本地存储(TLS)使您可以定义一个变量,每个线程都有其自己的单独...

    论文研究-用托管代码实现线程本地存储的三种方法 .pdf

    用托管代码实现线程本地存储的三种方法,王曼韬,,多线程环境下实现线程本地存储是多线程应用中的一种基本技术,本文详细论述了用托管代码实现线程本地存储的三种方法,并对这些方

    关于Linux线程的线程栈以及TLS

    本文描述LinuxNPTL的线程栈简要实现以及线程本地存储的原理,实验环境中Linux内核版本为2.6.32,glibc版本是2.12.1,Linux发行版为ubuntu,硬件平台为x86的32位系统。b.对于LinuxNPTL线程,有很多话题。本文挑选了...

    逆向工程权威指南.

    涉及X86/X64、ARM/ARM-64、MIPS、Java/JVM等重要话题,详细解析了Oracle RDBMS、Itanium、软件狗、LD_PRELOAD、栈溢出、ELF、Win32 PE文件格式、x86-64(第、critical sections、syscalls、线程本地存储TLS、地址...

    ELF -- 线程本地存储

    ELF Handling For Thread Local Storage

    逆向工程权威指南.下册

    涉及X86/X64、ARM/ARM-64、MIPS、Java/JVM等重要话题,详细解析了Oracle RDBMS、Itanium、软件狗、LD_PRELOAD、栈溢出、ELF、Win32 PE文件格式、x86-64(第、critical sections、syscalls、线程本地存储TLS、地址...

    逆向工程权威指南

    涉及X86/X64、ARM/ARM-64、MIPS、Java/JVM等重要话题,详细解析了Oracle RDBMS、Itanium、软件狗、LD_PRELOAD、栈溢出、ELF、Win32 PE文件格式、x86-64(第、critical sections、syscalls、线程本地存储TLS、地址...

    sakeInject:Windows PE - CC++ 中的 TLS(线程本地存储)注入器

    日本清酒C/C++ 中的 TLS(线程本地存储)注入器

    tls_src.zip_TLS

    线程本地存储(TLS)的一个类,可以实现多线程对共享资源的同步.

    Windows 内核情景分析--采用开源代码ReactOS (上册) part01

    5.11 线程本地存储TLS 421 5.12 进程挂靠 434 5.13 Windows的跨进程操作 442 5.14 Windows线程间的相互作用 450 第6章 进程间通信 467 6.1 概述 467 6.2 共享内存区(Section).. 469 6.3 线程的等待/唤醒...

    tls.rar_TLS_Thread Local Storage_thread local

    Simplify the Use of Thread Local Storage 关于线程本地存储的简单示范

    gls:快速goroutine本地存储

    gls 快速goroutine本地存储警告关于为什么在Go中实现和使用线程本地存储(实际上是goroutine本地存储)是一个坏主意,有大量的文档和讨论。 例如,请参阅和的 ,这是鼓励您解决需要goroutine本地存储的问题的方式。 ...

    python-3.7.0b5下載

    但现有的线程本地存储(TLS)API使用int来表示所有平台上的TLS密钥,但这既不符合POSIX标准,也不具备任何实际意义上的便携性。 PEP 539通过向CPython提供新的线程本地存储(TSS)API来改变这一点,该API取代了在...

    iocp udp 分布式队列无同步多核测试

    在程序中采用tls,使每个线程都有自己的本地队列,从而避免同步. 还没有测试的是chche伪共享造成的性能下降,在后面将测试. 设置代码中 DEFAULT_RUDP_BUFFER_SIZE = 32k(实际udp包远没有这么大),用vc6及vc8编译,在我...

    JavaScript的Zone实现Zone.js.zip

    Zone 是执行过程的上下文,可以在异步任务之间进行持久性传递,你可以把它当成是类似 Java 的 TLS 线程本地存储技术,只不过是用在 JavaScript 语言中。 示例代码: zone.run(function () {  zone.inTheZone = ...

    《Windows高级编程指南(第三版)》(含PASCAL例子)

    (Local Input State) TLSStat -- 在EXE模块中使用静态TLS TLSDyn & -- 在DLL模块中使用动态TLS SomeLib ModUse & -- 使用DLL中带有共享属性的PE节存储数据 Module DocStats -- 利用事件对象使多个线程协同工作 ...

Global site tag (gtag.js) - Google Analytics