thread_local & __thread & thread specific data
thread_local
和 __thread
首先来看一段代码
/*********************************************************
File Name:thread_local.cpp
Author: Abby Cin
Mail: abbytsing@gmail.com
Created Time: Thu 11 Aug 2016 07:34:25 PM CST
**********************************************************/
#include <iostream>
#include <threadpool>
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
pid_t gettid()
{
return static_cast<pid_t>(syscall(SYS_gettid));
}
#ifdef THREAD_LOCAL
#pragma message "`thread_local`"
thread_local int data = 2;
#else
#pragma message "`__thread`"
__thread int data = 2;
#endif
int main()
{
std::mutex mtx;
auto func = [&](const int x)
{
std::lock_guard<std::mutex> lck(mtx);
data *= x;
fprintf(stderr, "tid: %d\t data: %d\n", gettid(), data);
};
std::threadpool pool;
pool.add_task(func, 2);
pool.add_task(func, 4);
pool.add_task(func, 5);
pool.add_task(func, 6);
fprintf(stderr, "main: %d\t data: %d\n", gettid(), data);
}
输出
从图中看出,虽然使用了同一个变量,但是每个线程的输出并没有受到彼此的干扰,这就是__thread
和thread_local
的作用:每个线程共享同一个变量名,但是各自持有此变量的一份实例(thread local storage)。但是从图中似乎看不出有什么不同。
没错,这刚好是__thread
和thread_local
的交集。thread_local
是C++11中出现的一个关键字,__thread
是GCC中的一个标识符。
下面来比较一下异同 ghost的 markdown不支持表格:(
__thread:
只支持POD类型(Plain Old Data,对C++来说是C语言的遗留部分),类成员的静态变量,函数内的静态变量
初始值在声明确定(如果将上面代码中的
__thread int data = 2
改为__thread int data
的话,并在main函数中第一行为data赋值为2,那么,除了主线程输出为2外,其它线程都将输出0)需要连接器(ld)、动态连接器(ld.so)以及系统库(libc.so和libpthread.so)的支持
注意:当取地址符作用与thread_local变量时,得到的地址是运行时此变量在该线程中的实例的地址,并且在该线程结束后,任何指向此变量的指针都将失效(invalid)
thread_local:
支持POD类型,同时也支持动态初始化类型(含有构造、复制构造、析构的类型)
初始值在声明时确定(同上)
位于C++标准库中,跨平台支持
可以看出,在C++11之前是可以使用__thread
的,虽然有各种限制,在C++11及以后,就可以使用thread_local
替代__thread
了。
thread-specific data
同样,先看一段代码
/*********************************************************
File Name:thread_specific_data.cpp
Author: Abby Cin
Mail: abbytsing@gmail.com
Created Time: Thu 11 Aug 2016 09:01:11 PM CST
**********************************************************/
#include <iostream>
#include <threadpool>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
pid_t gettid()
{
return static_cast<pid_t>(syscall(SYS_gettid));
}
pthread_key_t key;
int main()
{
int data = 2;
pthread_key_create(&key, NULL);
pthread_setspecific(key, &data);
std::mutex mtx;
auto func = [&](int x)
{
std::lock_guard<std::mutex> lck(mtx);
int* data_ = static_cast<int*>(pthread_getspecific(key));
if(data_ == nullptr)
fprintf(stderr, "tid: %d\t data: null\n", gettid());
else
{
*data_ *= x;
pthread_setspecific(key, data_);
}
};
{
std::threadpool pool;
pool.add_task(func, 2);
pool.add_task(func, 4);
pool.add_task(func, 5);
pool.add_task(func, 6);
}
int* data_ = static_cast<int*>(pthread_getspecific(key));
if(data_ != nullptr)
fprintf(stderr, "main: %d\t data: %d\n", gettid(), *data_);
else
fprintf(stderr, "main: %d\t data: null\n", gettid());
pthread_key_delete(key);
}
输出如下:
可以发现,虽然几个线程都使用了同一个key,但是只在设置了数据的当前线程可以获得这个值。换句话说,这个值只在当前线程中才有(thread specific data)。
那么,这和前面的两个有什么联系呢?
首先,都是各个线程之前不会相互影响;其次,thread-specific data
是POSIX规范,因此相对于__thread
来说兼容性可能更好。但是,有着相似的feature,明显__thread
和thread_local
用起来要方便一些啊!