替換__thread的一種方式,實現TLS功能

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

 

其中有個問題,主線程的資源最後沒有被釋放,這是否是問題呢?你們能夠思考一下?

 

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

相關文章
相關標籤/搜索