TLS是因爲多線程編程帶來的產物,主要是爲了解決線程資源局部化,具體內容網上有不少介紹。有不少地方已經支持了該功能,但有些地方沒有,下面是GCC的一些介紹,反正具體看實際使用狀況:html
5.51 Thread-Local Storage編程
=========================api
Thread-local storage (TLS) is a mechanism by which variables are多線程
allocated such that there is one instance of the variable per extantapp
thread. The run-time model GCC uses to implement this originates in函數
the IA-64 processor-specific ABI, but has since been migrated to other測試
processors as well. It requires significant support from the linkerui
(`ld'), dynamic linker (`ld.so'), and system libraries (`libc.so' andthis
`libpthread.so'), so it is not available everywhere.lua
At the user level, the extension is visible with a new storage class
keyword: `__thread'. For example:
__thread int i;
extern __thread struct state s;
static __thread char *p;
The `__thread' specifier may be used alone, with the `extern' or
`static' specifiers, but with no other storage class specifier. When
used with `extern' or `static', `__thread' must appear immediately
after the other storage class specifier.
The `__thread' specifier may be applied to any global, file-scoped
static, function-scoped static, or static data member of a class. It
may not be applied to block-scoped automatic or non-static data member.
When the address-of operator is applied to a thread-local variable, it
is evaluated at run-time and returns the address of the current thread's
instance of that variable. An address so obtained may be used by any
thread. When a thread terminates, any pointers to thread-local
variables in that thread become invalid.
No static initialization may refer to the address of a thread-local
variable.
In C++, if an initializer is present for a thread-local variable, it
must be a CONSTANT-EXPRESSION, as defined in 5.19.2 of the ANSI/ISO C++
standard.
See ELF Handling For Thread-Local Storage
(http://people.redhat.com/drepper/tls.pdf) for a detailed explanation of
the four thread-local storage addressing models, and how the run-time
is expected to function.
爲了防止現有資源不支持TLS的狀況,下面提供一種繞開__thread的一種實現。
//類文件Tls.h:
#ifndef __SAP_UTIL_TLS_H_
#define __SAP_UTIL_TLS_H_
#include <pthread.h>
#include "Tlsconf.h"
typedef struct pthread_atexit
{
int key; //線程局部變量標記
void (*free_fn)(void*); //線程結束時資源釋放回調函數
void *arg; //線程局部變量地址
}pthread_atexit_t;
typedef std::list<pthread_atexit_t *> TlsList; //一個線程的線程局部變量構成一條鏈
class Tls
{
public:
Tls();
~Tls();
static char *pthread_atexit_get_buf(variable_key_t key, int len); //主要外部接口,得到線程局部變量
static int pthread_atexit_add(void *arg, variable_key_t key, void (*free_fn)(void*)); //新的線程局部變量使用該接口進行存儲
static int pthread_atexit_remove(void *arg, variable_key_t key, void (*free_fn)(void*)); //刪除特定的某個線程局部變量,暫時沒啥用
protected:
static void pthread_atexit_done(void *arg); //線程結束回調函數,用於回收該線程全部線程局部變量資源
static void pthread_atexit_init(void); //設置線程結束時的回調函數
static void pthread_atexit_release(void *pbuf); //資源釋放函數
static char *pthread_atexit_get_buf_from_list(TlsList *ptlslist, variable_key_t key); //從線程局部變量鏈表當中找到key值的變量
protected:
static pthread_key_t _pthread_atexit_key; //線程存儲鍵值,每一個線程經過它來讀取數據鏈
static pthread_once_t _pthread_atexit_control_once; //初始化狀態標誌,pthread_once使用
};
#endif
#endif
//外部接口聲明tls_api.h:
#ifndef _TLS_API_H_
#define _TLS_API_H_
#include "Tlsconf.h"
#ifdef __cplusplus
extern "C"
{
#endif
char* get_buf(variable_key_t key, int len);
#ifdef __cplusplus
}
#endif
#endif
//參數鍵值表,用於標記哪一個函數中的哪一個參數Tlsconf.h:
#ifndef _TLS_CONF_H
#define _TLS_CONF_H
typedef enum
{
ENUM_0,
ENUM_1,
ENUM_2,
ENUM_3,
ENUM_4,
ENUM_5,
ENUM_6,
ENUM_7,
}variable_key_t;
#endif
//類實現及外部接口Tls.cpp:
#include <sys/syscall.h>
#include <list>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Tls.h"
#include "Tlsconf.h"
#include "tls_api.h"
using namespace std;
//#define gettid() syscall(__NR_gettid)
#define TLS_OUT_OF_INDEXES 0xffffffff
pthread_key_t Tls::_pthread_atexit_key = TLS_OUT_OF_INDEXES;
pthread_once_t Tls::_pthread_atexit_control_once = PTHREAD_ONCE_INIT;
Tls::Tls()
{
//_pthread_atexit_key = TLS_OUT_OF_INDEXES;
//_pthread_atexit_control_once = PTHREAD_ONCE_INIT;
}
Tls::~Tls()
{
}
char* Tls::pthread_atexit_get_buf_from_list(TlsList *ptlslist, variable_key_t key)
{
pthread_atexit_t *id_ptr=NULL;
if (NULL == ptlslist)
{
return NULL;
}
for (TlsList::iterator iter=ptlslist->begin(); iter !=ptlslist->end(); ++iter)
{
id_ptr = *iter;
if (id_ptr == NULL)
continue;
if (key == id_ptr->key) //經過key值區分變量
{
return (char*)(id_ptr->arg);
}
}
return NULL;
}
char* Tls::pthread_atexit_get_buf(variable_key_t key, int len)
{
TlsList* id_list;
char* ptrtmp = NULL;
int iret = -1;
if (len <= 0)
{
return NULL;
}
pthread_once(&_pthread_atexit_control_once, pthread_atexit_init); //該函數只調用一次pthread_atexit_init
if (_pthread_atexit_key == (pthread_key_t) TLS_OUT_OF_INDEXES)
{
printf("%s(%d): _pthread_atexit_key(%d) invalid\n", __func__, __LINE__, _pthread_atexit_key);
return NULL;
}
id_list = (TlsList*) pthread_getspecific(_pthread_atexit_key);
ptrtmp = pthread_atexit_get_buf_from_list(id_list, key);
if (NULL != ptrtmp)
{
return ptrtmp;
}
ptrtmp = (char*)malloc(len);
if (NULL == ptrtmp)
{
return NULL;
}
memset(ptrtmp, 0, len);
iret = pthread_atexit_add(ptrtmp, key, pthread_atexit_release);
if (-1 == iret)
{
free(ptrtmp);
return NULL;
}
return ptrtmp;
}
void Tls::pthread_atexit_done(void *arg)
{
TlsList *id_list = (TlsList*) arg;
pthread_atexit_t *id_ptr=NULL;
//printf("invoke Tls::pthread_atexit_done(): tid=%ld\n",gettid());
for(TlsList::iterator iter=id_list->begin(); iter !=id_list->end(); ++iter)
{
id_ptr = *iter;
if (id_ptr == NULL)
continue;
printf("pthread(%u) realease resouce %d!\n", pthread_self(), id_ptr->key);
if (id_ptr->free_fn)
id_ptr->free_fn(id_ptr->arg);
delete id_ptr;
}
delete id_list;
}
void Tls::pthread_atexit_init(void)
{
pthread_key_create(&_pthread_atexit_key, pthread_atexit_done);
}
void Tls::pthread_atexit_release(void *pbuf)
{
if (NULL == pbuf)
{
return;
}
free((char*)pbuf);
return;
}
int Tls::pthread_atexit_add(void *arg, variable_key_t key, void (*free_fn)(void*))
{
const char *myname = "pthread_atexit_add";
pthread_atexit_t *id;
TlsList *id_list;
if (arg == NULL)
{
return 0;
}
id = new pthread_atexit_t;
if (id == NULL)
{
printf("%s(%d): new pthread_atexit_t error\n", myname, __LINE__);
return -1;
}
id->key = key;
id->free_fn = free_fn;
id->arg = arg;
id_list = (TlsList*) pthread_getspecific(_pthread_atexit_key);
if (id_list == NULL)
{
id_list = new TlsList();
if (pthread_setspecific(_pthread_atexit_key, id_list) != 0)
{
printf("%s(%d): pthread_setspecific error, key(%d)\n", myname, __LINE__, _pthread_atexit_key);
goto errExit;
}
}
id_list->push_back(id);
printf("pthread(%u) get resouce %d!\n", pthread_self(), id->key);
return 0;
errExit:
if (id)
{
delete id;
}
if (id_list)
{
delete id_list;
}
return -1;
}
int Tls::pthread_atexit_remove(void *arg, variable_key_t key, void (*free_fn)(void*))
{
const char *myname = "pthread_atexit_remove";
TlsList *id_list;
if (arg == NULL)
{
return (-1);
}
if (_pthread_atexit_key == (pthread_key_t) TLS_OUT_OF_INDEXES)
{
printf("%s(%d): _pthread_atexit_key(%d) invalid\n", myname, __LINE__, _pthread_atexit_key);
return (-1);
}
id_list = (TlsList*) pthread_getspecific(_pthread_atexit_key);
if (id_list == NULL)
{
printf("%s(%d): _pthread_atexit_key(%d) no exist in tid(%lu)\n", myname, __LINE__, _pthread_atexit_key,(unsigned long) pthread_self());
return (-1);
}
pthread_atexit_t *id_ptr =NULL;
TlsList::iterator iter=id_list->begin();
for(; iter !=id_list->end(); ++iter)
{
id_ptr = *iter;
if (id_ptr == NULL)
continue;
if (id_ptr->free_fn == free_fn && id_ptr->arg == arg)
{
break;
}
}
if(id_ptr != NULL)
{
id_list->erase(iter);
delete id_ptr;
}
return (0);
}
char* get_buf(variable_key_t key, int len)
{
static Tls tls;
return tls.pthread_atexit_get_buf(key, len);
}
這個類裏用到了幾個使人蛋疼的庫函數,初看之下還覺得是「山寨」的。不過幸虧man都能找到,每一個函數的功能就不具體介紹了,不少地方都有介紹的。
這個類主要採用了這樣的一種結構:每一個線程擁有一個TlsList鏈,它存儲了該線程使用的全部的線程局部變量。每一個線程能夠經過pthread_setspecific存儲TlsList,也能夠經過pthread_getspecific讀出TlsList,進而經過variable_key_t key來訪問TlsList中的數據。Key與線程無關,每一個函數中的每一個線程局部變量須要擁有一個獨立的key值。所以,在實際使用時一個增長一個線程局部變量就須要在Tlsconf.h中加一個字段,一樣每刪除一個變量都須要將Tlsconf.h中對應的key值刪除。線程在結束時,會自動調用回調函數,遍歷該線程的TlsList,調用每一個資源的清理回調函數。
Tls類中基本全部的成員都是靜態變量,一來是爲了方便,由於成員函數裏用到了其它成員函數的地址,不用靜態成員不太好處理;二來這樣也已經知足使用需求了,具體爲何,你們能夠本身思考一下,歡迎一塊兒討論。
下面是測試函數,主要是看內存的申請、使用及內存釋放:
//Test4:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "pthread.h"
#include "TLS/Tlsconf.h"
#include "TLS/tls_api.h"
#define ERROR -1
#define OK 0
void test_fn2(int a)
{
char *psz = (char*)get_buf(ENUM_1, 32);
printf("thread(%u) psz: %s\n", pthread_self(), psz);
memset(psz, 0, 32);
sprintf(psz, "%u_%d", pthread_self(), a);
return;
}
void* test_fn_main2(void* arg)
{
int i = 0;
for (i = 0; i < 3; i++)
{
test_fn2(i);
sleep(1);
}
return;
}
void test_fn1(void)
{
int *reti = (int*)get_buf(ENUM_0, sizeof(int));
printf("thread(%u) reti: %d\n", pthread_self(), *reti);
(*reti)++;
return;
}
void* test_fn_main1(void* arg)
{
int i = 0;
for (i = 0; i < 3; i++)
{
test_fn1();
sleep(1);
}
return;
}
int main()
{
int iRet = ERROR;
int i = 0;
pthread_t tid;
printf("test start!\n");
for (i = 0; i < 5; i++)
{
iRet = pthread_create(&tid, NULL, test_fn_main1, NULL);
if (OK != iRet)
{
printf("pthread_create error!\n");
return ERROR;
}
iRet = pthread_create(&tid, NULL, test_fn_main2, NULL);
if (OK != iRet)
{
printf("pthread_create error!\n");
return ERROR;
}
}
sleep(20);
return OK;
}
//result
[root@localhost 20130713]# ./tls4
test start!
pthread(3086523280) get resouce 0!
thread(3086523280) reti: 0
pthread(3076033424) get resouce 1!
thread(3076033424) psz:
pthread(3065543568) get resouce 0!
thread(3065543568) reti: 0
pthread(3055053712) get resouce 1!
thread(3055053712) psz:
pthread(3044563856) get resouce 0!
thread(3044563856) reti: 0
pthread(3034074000) get resouce 1!
thread(3034074000) psz:
pthread(3023584144) get resouce 0!
thread(3023584144) reti: 0
pthread(3013094288) get resouce 1!
thread(3013094288) psz:
pthread(3002604432) get resouce 0!
thread(3002604432) reti: 0
pthread(2992114576) get resouce 1!
thread(2992114576) psz:
thread(3086523280) reti: 1
thread(3076033424) psz: 3076033424_0
thread(3065543568) reti: 1
thread(3055053712) psz: 3055053712_0
thread(3044563856) reti: 1
thread(3034074000) psz: 3034074000_0
thread(3023584144) reti: 1
thread(3013094288) psz: 3013094288_0
thread(3002604432) reti: 1
thread(2992114576) psz: 2992114576_0
thread(3086523280) reti: 2
thread(3076033424) psz: 3076033424_1
thread(3065543568) reti: 2
thread(3055053712) psz: 3055053712_1
thread(3044563856) reti: 2
thread(3034074000) psz: 3034074000_1
thread(3023584144) reti: 2
thread(3013094288) psz: 3013094288_1
thread(3002604432) reti: 2
thread(2992114576) psz: 2992114576_1
pthread(3086523280) realease resouce 0!
pthread(3076033424) realease resouce 1!
pthread(3065543568) realease resouce 0!
pthread(3055053712) realease resouce 1!
pthread(3044563856) realease resouce 0!
pthread(3034074000) realease resouce 1!
pthread(3023584144) realease resouce 0!
pthread(3013094288) realease resouce 1!
pthread(3002604432) realease resouce 0!
pthread(2992114576) realease resouce 1!
//Test5:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "pthread.h"
#include "TLS/Tlsconf.h"
#include "TLS/tls_api.h"
#define ERROR -1
#define OK 0
void test_fn2(int a)
{
char *psz = (char*)get_buf(ENUM_1, 32);
printf("thread(%u) psz: %s\n", pthread_self(), psz);
memset(psz, 0, 32);
sprintf(psz, "%u_%d", pthread_self(), a);
return;
}
void test_fn1(int a)
{
int *reti = (int*)get_buf(ENUM_0, sizeof(int));
printf("thread(%u) reti: %d\n", pthread_self(), *reti);
(*reti)++;
test_fn2(a);
return;
}
void* test_fn_main1(void* arg)
{
int i = 0;
for (i = 0; i < 3; i++)
{
test_fn1(i);
sleep(1);
}
return;
}
int main()
{
int iRet = ERROR;
int i = 0;
pthread_t tid;
printf("test start!\n");
for (i = 0; i < 5; i++)
{
iRet = pthread_create(&tid, NULL, test_fn_main1, NULL);
if (OK != iRet)
{
printf("pthread_create error!\n");
return ERROR;
}
}
sleep(20);
return OK;
}
//result
[root@localhost 20130713]# ./tls5
test start!
pthread(3086138256) get resouce 0!
thread(3086138256) reti: 0
pthread(3086138256) get resouce 1!
thread(3086138256) psz:
pthread(3075648400) get resouce 0!
thread(3075648400) reti: 0
pthread(3075648400) get resouce 1!
thread(3075648400) psz:
pthread(3065158544) get resouce 0!
thread(3065158544) reti: 0
pthread(3065158544) get resouce 1!
thread(3065158544) psz:
pthread(3054668688) get resouce 0!
thread(3054668688) reti: 0
pthread(3054668688) get resouce 1!
thread(3054668688) psz:
pthread(3044178832) get resouce 0!
thread(3044178832) reti: 0
pthread(3044178832) get resouce 1!
thread(3044178832) psz:
thread(3086138256) reti: 1
thread(3086138256) psz: 3086138256_0
thread(3075648400) reti: 1
thread(3075648400) psz: 3075648400_0
thread(3065158544) reti: 1
thread(3065158544) psz: 3065158544_0
thread(3054668688) reti: 1
thread(3054668688) psz: 3054668688_0
thread(3044178832) reti: 1
thread(3044178832) psz: 3044178832_0
thread(3086138256) reti: 2
thread(3086138256) psz: 3086138256_1
thread(3075648400) reti: 2
thread(3075648400) psz: 3075648400_1
thread(3065158544) reti: 2
thread(3065158544) psz: 3065158544_1
thread(3054668688) reti: 2
thread(3054668688) psz: 3054668688_1
thread(3044178832) reti: 2
thread(3044178832) psz: 3044178832_1
pthread(3086138256) realease resouce 0!
pthread(3086138256) realease resouce 1!
pthread(3075648400) realease resouce 0!
pthread(3075648400) realease resouce 1!
pthread(3065158544) realease resouce 0!
pthread(3065158544) realease resouce 1!
pthread(3054668688) realease resouce 0!
pthread(3054668688) realease resouce 1!
pthread(3044178832) realease resouce 0!
pthread(3044178832) realease resouce 1
其中有個問題,主線程的資源最後沒有被釋放,這是否是問題呢?你們能夠思考一下?