TLS--线程局部存储 学习

参考:https://www.cnblogs.com/stli/archive/2010/11/03/1867852.html TLS--线程局部存储

https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B%E5%B1%80%E9%83%A8%E5%AD%98%E5%82%A8/10484278?fr=aladdin 

概念:线程局部存储(Thread Local Storage,TLS)用来将数据与一个正在执行的指定线程关联起来。 

功能:主要是为了避免多个线程同时访存同一全局或静态变量时所导致的冲突,尤其是多个线程同时需要修改这一变量时。通过TLS机制,为每一个使用该全局变量的线程都提供一个变量值的副本,每一个线程均可独立改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就像每一个线程都完全拥有该变量。从全局变量的角度上来看,就像一个全局变量被克隆成了多份副本,每一份副本都可被一个线程独立地改变。如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要TLSTLS主要用于线程中必须使用全局或静态变量的情况。 

方法一:

系统中每个进程都有一组正在使用标志(in-use flags),每个标志可以被设为FREE或INUSE,表示该TLS元素是否正在被使用。进程中的线程是通过使用一个数组来保存与线程相关联的数据的,数组由TLS_MINIMUM_AVAILABLE(WINNT.H文件中定义为64)个元素组成。每一个线程中都有其数组,数组中的每一个元素都能保存一个32位的值。通过一组API来使用动态TLS。

1)使用动态TLS,必须先调用TlsAlloc:这个函数让系统对进程中的位标志进行检索并找到一个FREE标志,然后系统会将该标志从FREE改为INUSE并返回该标志在位数组中的索引。一个DLL(或应用程序)通常将这个索引保存在一个全局变量中。

如果TlsAlloc无法在列表中找到一个FREE标志,会返回TLS_OUT_OF_INDEXES(在WinBase.h中定义为0xFFFFFFFF)。

当系统创建一个线程的时候,会分配TLS_MINIMUM_AVAILABLE个PVOID值,初始化为0,并与线程关联起来。每个线程都有自己的PVOID数组,数组中的每个PVOID可以保存任意值。在能够将信息保存到线程的PVOID数组中之前,我们必须知道数组中的哪个索引可供使用---这就是调用TlsAlloc的目的。TlsAlloc为我们预定了一个索引,如果为2,即TlsAlloc返回值为2,那么无论是进程中当前正在运行的线程,还是今后可能会创建的线程,都不能再使用该索引2了。

2)把一个值放到线程的PVOID数组中,用TlsSetValue:当一个线程调用成功时,它会修改自己的PVOID数组,但它无法修改另一个线程的TLS值。用TlsSetValue时,须传入TlsAlloc返回的索引。Windows为了效率牺牲了对输入值的错误检测。

3)从线程的数组中取回一个值,用TlsGetValue:TlsGetValue只会查看属于调用线程的数组,须传入TlsAlloc返回的索引。

4)当不再需要一个已经预定的TLS元素时,用TlsFree告诉系统这个TLS元素现在不需要了,函数会将进程内的位标志数组中对应的INUSE标志重新设回FREE。此外,函数还会将所有线程中该元素的内容设为0.

通常DLL使用TLS会在DllMain函数处理DLL_PROCESS_ATTACH的时候调用TlsAlloc,处理DLL_PROCESS_DETACH时调用TlsFree。而TlsSetValue和TlsGetValue的调用最有可能发生在DLL提供的其他函数中。应用程序中一般在需要时才添加TLS。

方法二

直接声明这个变量是各个线程有自己拷贝的线程局部静态变量

__declspec( thread ) int var_name;

但在VistaServer 2008之前的操作系统,仅限于在应用程序的主进程(.exe)以及与主进程一起装入内存的动态连接库(.dll),才能正常装入本方法所声明的线程静态存储。

linux实现

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

int pthread_key_delete(pthread_key_t key);

void *pthread_getspecific(pthread_key_t key);

int pthread_setspecific(pthread_key_t key, const void *value);

WINDOW下例子如下:建一个对话框程序拷贝过去可以编译运行: 

#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
DWORD dwTlsIndex,dwTlsIndex2;
VOID ErrorExit(LPSTR); 
VOID ErrorExit (LPSTR lpszMessage)
{
    TRACE1("==============%s\n", lpszMessage);
    ExitProcess(0);
}
VOID CommonFunc(VOID)
{
    LPVOID lpvData; 
    // Retrieve a data pointer for the current thread. 
    lpvData = TlsGetValue(dwTlsIndex);
    if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))
        ErrorExit("TlsGetValue error"); 
    // Use the data stored for the current thread. 
    TRACE2("delay common: thread %d: lpvData=%lx\n",
        GetCurrentThreadId(), lpvData); 
    Sleep(3000);
}    
static char* ss_lpvData = NULL; //全局唯一的静态变量
static int s_iThread = 0;//全局唯一的静态变量
__declspec(thread) static int s_iThreadId = 0;//只对当前线程是唯一的静态变量 每一线程可以不同。

__declspec(thread) static char* s_pThreadData = NULL;//只对当前线程是唯一的静态变量 每一线程可以不同。须独立申请与释放

DWORD WINAPI ThreadFunc(VOID)
{
    if (s_pThreadData == NULL)
    {
        s_pThreadData =(char*)calloc(256, sizeof(char));
        TRACE("线程局部变量申请一次地址:=%lx \n", s_pThreadData); 
    }


    s_iThreadId=GetCurrentThreadId();

    sprintf(s_pThreadData, "-------当前静态局部存储值-----ThreadId=%d -----\n"
        , s_iThreadId);
    TRACE(s_pThreadData);
    // Initialize the TLS index for this thread. 
    if(ss_lpvData == NULL)
    {
        ss_lpvData = (char*) LocalAlloc(LPTR, 256);
        sprintf(ss_lpvData, "----全局变量申请一次地址:=%lx---全局静态存储值-----ThreadId=%d -----\n",
            ss_lpvData, s_iThreadId);
        TRACE( ss_lpvData);
    }


    if (! TlsSetValue(dwTlsIndex, ss_lpvData))
        ErrorExit("TlsSetValue error"); 

    if (! TlsSetValue(dwTlsIndex2, (LPVOID)s_iThread++))
        ErrorExit("TlsSetValue error");

    TRACE1("s_iThread=%d \n", s_iThread); 
 
    CommonFunc(); 
    TRACE(s_pThreadData);
    memset(s_pThreadData,0, 256);

    // Release the dynamic memory before the thread returns. 
    int iThread = (int)TlsGetValue(dwTlsIndex2);


    sprintf(s_pThreadData, "延时后-动态局部存储值-----iThread=%d -全局静态s_iThread=%d%----\n"
        , iThread,s_iThread);
    TRACE(s_pThreadData);

//    TRACE1("s_iThread=%d\n",s_iThread); TRACE1及3中串不可有中文,否则报错。

//     if (ss_lpvData != 0)//全局变量,不可在这里释放
//         LocalFree((HLOCAL) ss_lpvData); 

    free(s_pThreadData);//线程局变量,应该在此释放。
    s_pThreadData = NULL;
    return 0;
}
void CdddtestDlg::OnBnClickedButton1()
{

        DWORD IDThread;
        HANDLE hThread[THREADCOUNT];
        int i; 
        // Allocate a TLS index. 
        if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)//可以使用多个ID
            ErrorExit("TlsAlloc failed"); 

        if ((dwTlsIndex2 = TlsAlloc()) == TLS_OUT_OF_INDEXES)
            ErrorExit("TlsAlloc failed2"); 
        // Create multiple threads. 
        for (i = 0; i < THREADCOUNT; i++)
        {
            hThread[i] = CreateThread(NULL, // default security attributes
                0,                           // use default stack size
                (LPTHREAD_START_ROUTINE) ThreadFunc, // thread function
                NULL,                    // no thread function argument
                0,                       // use default creation flags
                &IDThread);              // returns thread identifier 
            // Check the return value for success.
            if (hThread[i] == NULL)
                ErrorExit("CreateThread error\n");
        } 
        for (i = 0; i < THREADCOUNT; i++)
            WaitForSingleObject(hThread[i], INFINITE); 
        TlsFree(dwTlsIndex); 

        TlsFree(dwTlsIndex2); 

        if (ss_lpvData != 0)
            LocalFree((HLOCAL) ss_lpvData);

}
结果如下:

线程局部变量申请一次地址:=6dfd30 
-------当前静态局部存储值-----ThreadId=3332 -----
----全局变量申请一次地址:=4a4fa0---全局静态存储值-----ThreadId=3332 -----
s_iThread=1 
delay common: thread 3332: lpvData=4a4fa0
线程局部变量申请一次地址:=6dfe70 
-------当前静态局部存储值-----ThreadId=2136 -----
s_iThread=2 
delay common: thread 2136: lpvData=4a4fa0
线程局部变量申请一次地址:=6dbf10 
-------当前静态局部存储值-----ThreadId=3236 -----
s_iThread=3 
delay common: thread 3236: lpvData=4a4fa0
线程局部变量申请一次地址:=6dc2a0 
-------当前静态局部存储值-----ThreadId=3780 -----
s_iThread=4 
delay common: thread 3780: lpvData=4a4fa0
-------当前静态局部存储值-----ThreadId=3332 -----
延时后-动态局部存储值-----iThread=0 -全局静态s_iThread=4
The thread 'Win32 Thread' (0xd04) has exited with code 0 (0x0).
-------当前静态局部存储值-----ThreadId=2136 -----
延时后-动态局部存储值-----iThread=1 -全局静态s_iThread=4
The thread 'Win32 Thread' (0x858) has exited with code 0 (0x0).
-------当前静态局部存储值-----ThreadId=3236 -----
延时后-动态局部存储值-----iThread=2 -全局静态s_iThread=4
The thread 'Win32 Thread' (0xca4) has exited with code 0 (0x0).
-------当前静态局部存储值-----ThreadId=3780 -----
延时后-动态局部存储值-----iThread=3 -全局静态s_iThread=4
The thread 'Win32 Thread' (0xec4) has exited with code 0 (0x0).

猜你喜欢

转载自blog.csdn.net/DANFBAORE/article/details/84618402