线程本地存储

对于多线程程序,所有线程共享全局和静态变量,任何线程使用变量之后都会在其他线程可见,因此对于执行顺序非常重要的场景,需要使用多重方式来进行同步确保线程安全。但是,如果希望每个线程单独拥有一个全局或静态变量,所有线程都可以使用它,但是在每个线程中是单独存储的,那么就需要使用线程本地存储。

pthread库的实现

经典的pthread线程库提供了对线程本地存储的完全支持,具体需要使用如下三个函数:

//将需要共享的变量转换为void*指针,绑定到pthread_key_t关联的全局对象中
int pthread_setspecific(pthread_key_t key, const void *value);

//从绑定到pthread_key_t类型的全局变量中获取需要的数据,返回类型需要从void*转换为实际类型
void *pthread_getspecific(pthread_key_t key);

//创建一个全局关联变量,第二个参数是指定对用户关联的数据进行释放的handler
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

具体使用步骤:

  1. 创建一个类型为 pthread_key_t 类型的变量。
  2. 调用 pthread_key_create()来创建该变量。该函数有两个参数,第一个参数就是上面声明的 pthread_key_t变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。
  3. 当线程中需要存储特殊值的时候,调用 pthread_setspcific()。该函数有两个参数,第一个为前面声明的 pthread_key_t 变量,第二个为 void*变量,这样你可以存储任何类型的值。
  4. 如果需要取出所存储的值,调用 pthread_getspecific()。该函数的参数为前面提到的 pthread_key_t 变量,该函数返回 void *类型的值。

C++封装

针对这些C风格函数操作,下面使用C++模板封装了一个范型版本。

template<typename T>
class ThreadSpecificUtil {
public:
    static void init() {
        pthread_once(&_s_once, initKey);
    }
    static T * get() {
        return renterpret_cast<T *>(
            pthread_getspecific(_s_key));
    }
    static int set(T *data) {
        int ret = pthread_setspecific(
            _s_key,
            reinterpret_cast<void *>(data));
        return ret;
    }

private:
    static void initKey() {
        pthread_key_create(&_s_key, dataDestructor);
    }
    static void dataDestructor(void *p) {
        if (NULL != p) {
            T *data = reinterpret_cast<T *>(p);
            delete data;
        }
    }
private:
    static pthread_key_t  _s_key;
    static pthread_once_t _s_once;
};
template<typename T>
pthread_key_t ThreadSpecificUtil<T>::_s_key;
template<typename T>
pthread_once_t ThreadSpecificUtil<T>::_s_once = PTHREAD_ONCE_INIT;

上述使用的pthread_once函数用来完成初始化工作,创建一个全局的pthread_key_t类型的变量,保证多个线程中只有一个来完成这个初始化工作。

C++11

C++11标准新添加了thread_local关键字,用来标识新的存储类型。

The storage class specifiers are a part of the decl-specifier-seq of a name’s declaration syntax. Together with the scope of the name, they control two independent properties of the name: Its storage duration and its linkage.
auto - automatic storage duration.(until C++11)
register - automatic storage duration. Also hints to the compiler to place the object in the processor’s register. (deprecated)(until C++17)
static - static or thread storage duration and internal linkage
extern - static or thread storage duration and external linkage
thread_local - thread storage duration.(since C++11)
Only one storage class specifier may appear in a declaration except that thread_local may be combined with static or with extern (since C++11)

这个关键字用来再变量声明的时候进行说明的一个语法,与作用域修饰符一起来决定变量的存储周期作用域,可以与static和extern复合修饰变量。
经过这个关键字定义的变量自动就成为了与线程关联的本地变量,每个线程都有一份拷贝,在线程创建时创建变量,线程销毁时析构变量。示例如下:

thread_local int j = 0;
void foo()
  {
  m.lock();
  j++; // j is now 1, no matter the thread. j is local to this thread.
  m.unlock();
  }
void func()
  {
  j = 0;
  std::thread t1(foo);
  std::thread t2(foo);
  t1.join();
  t2.join();
 // j still 0. The other "j"s were local to the threads
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值