TLS是由于多线程编程带来的产物,主要是为了解决线程资源局部化,具体内容网上有很多介绍。有很多地方已经支持了该功能,但有些地方没有,下面是GCC的一些介绍,反正具体看实际使用情况:

5.51 Thread-Local Storage

=========================

Thread-local storage (TLS) is a mechanism by which variables are

allocated such that there is one instance of the variable per extant

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 linker

(`ld'), dynamic linker (`ld.so'), and system libraries (`libc.so' and

`libpthread.so'), so it is not available everywhere.

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

其中有个问题,主线程的资源最后没有被释放,这是不是问题呢?大家可以思考一下?

参考:http://www.searchtb.com/2012/09/tls.html

替换__thread的一种方式,实现TLS功能的更多相关文章

  1. Python3中发邮件emal(明文/SSL/TLS三种方式)

    #!/usr/bin/env python #-*- coding:utf-8 -*- #Author:lzd import smtplib from email.mime.text import M ...

  2. js replace 全局替换 以表单的方式提交参数 判断是否为ie浏览器 将jquery.qqFace.js表情转换成微信的字符码 手机端省市区联动 新字体引用本地运行可以获得,放到服务器上报404 C#提取html中的汉字 MVC几种找不到资源的解决方式 使用Windows服务定时去执行一个方法的三种方式

    js replace 全局替换   js 的replace 默认替换只替换第一个匹配的字符,如果字符串有超过两个以上的对应字符就无法进行替换,这时候就要进行一点操作,进行全部替换. <scrip ...

  3. Windows10-UWP中设备序列显示不同XAML的三种方式[3]

    阅读目录: 概述 DeviceFamily-Type文件夹 DeviceFamily-Type扩展 InitializeComponent重载 结论 概述 Windows10-UWP(Universa ...

  4. .NET 实现并行的几种方式(四)

    本随笔续接:.NET 实现并行的几种方式(三) 八.await.async - 异步方法的秘密武器 1) 使用async修饰符 和 await运算符 轻易实现异步方法 前三篇随笔已经介绍了多种方式.利 ...

  5. 【整理】Linux下中文检索引擎coreseek4安装,以及PHP使用sphinx的三种方式(sphinxapi,sphinx的php扩展,SphinxSe作为mysql存储引擎)

          一,软件准备 coreseek4.1 (包含coreseek测试版和mmseg最新版本,以及测试数据包[内置中文分词与搜索.单字切分.mysql数据源.python数据源.RT实时索引等测 ...

  6. 预处理(防止sql注入的一种方式)

    <!--- 预处理(预编译) ---><?php/* 防止 sql 注入的两种方式: 1. 人为提高代码的逻辑性,使其变得更严谨,滴水不漏. 比如说 增加判断条件,增加输入过滤等,但 ...

  7. Android 数据存储五种方式

    1.概述 Android提供了5种方式来让用户保存持久化应用程序数据.根据自己的需求来做选择,比如数据是否是应用程序私有的,是否能被其他程序访问,需要多少数据存储空间等,分别是: ① 使用Shared ...

  8. C#-WinForm-打开其他窗体的三种方式-Show()、设置Owner()、ShowDialog()

    打开其他窗体的三种方式 Show - 例如登入界面进入主页面,直接将主页面展示出来,两个窗体互不影响 public partial class Form1 : Form { public Form1( ...

  9. 简介C#读取XML的两种方式

    简介C#读取XML的两种方式 作者: 字体:[增加 减小] 类型:转载 时间:2013-03-03 在程序中访问进而操作XML文件一般有两种模型,分别是使用DOM(文档对象模型)和流模型,使用DOM的 ...

随机推荐

  1. iOS8自适应布局视频教程

    联系:http://www.elsyy.com/course/6480 这是颐和园最近录制iOS8视频课程.简介iOS8出现在自适应布局. 本教程的书,颐和园<ios8 swift编程指南> ...

  2. HBase文件格式演变之路

    Apache HBase是Hadoop的分布式开源的存储管理工具,很适合随机实时的io操作. 我们知道,Hadoop的Sequence File是一个顺序读写,批量处理的系统.可是为什么HBase能做 ...

  3. 让Sqlite脱离VC++ Runtime独立执行

    前段时间在开发OrayTalk(傲瑞通企业即时通信系统)的聊天记录模块时用到了Sqlite,这是我第一次接触和使用Sqlite,整体感觉还是很不错的.这里把我使用Sqlite的经验跟大家分享一下. 一 ...

  4. PHP 调用微信JS-SDK 开发详解 [网摘]

    一:准备文件,并将文件置于网站根目录下 access_token.json {"access_token":"","expire_time" ...

  5. IE中的事件对象

    IE中的事件对象 1)type属性 用于获取事件类型 2)srcElement属性 用于获取事件的目标 3)cancelBubble属性 用于阻止事件冒泡 设置为true表示阻止事件冒泡  设置为fa ...

  6. How to: Installshield做安装包时如何添加文件

    原文:How to: Installshield做安装包时如何添加文件 我一直以为这不是一个问题,可是没想到在几个群内,对于如何向安装包添加文件不解的大有人在,今日稍暇,整理成篇,以供参考 首先我想再 ...

  7. JS获取标签方法及兼容处理

    document.getElementById('Id名');  // 所有浏览器 document.getElementsByTagName('标签名'); // 所有浏览器 document.ge ...

  8. sql点滴41—MyISAM 和 InnoDB 讲解

    原文:sql点滴41-MyISAM 和 InnoDB 讲解 InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型,这两个表类型各有优劣,视具体应用而定.基本的差别为:MyISAM类型 ...

  9. 4GB内存原32位系统(x86)取舍问题,显卡共享内存Win8.1完全不用担心

    情景:集成显卡 配置: 4G显示3.25GB 此时系统自动将用不到的系统完全共享给显卡(768MB而不是256): 看显卡适配器信息,完全共享给了显卡 解说:上图总可用图形内存 = 图2中备用 + 硬 ...

  10. [翻译]初识SQL Server 2005 Reporting Services Part 2

    原文:[翻译]初识SQL Server 2005 Reporting Services Part 2 在Part 1文章中我们对SQL Server Reporting Services 2005(S ...