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
从图中看出,虽然使用了同一个变量,但是每个线程的输出并没有受到彼此的干扰,这就是__threadthread_local的作用:每个线程共享同一个变量名,但是各自持有此变量的一份实例(thread local storage)。但是从图中似乎看不出有什么不同。

没错,这刚好是__threadthread_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);
}

输出如下:
thread-specific data
可以发现,虽然几个线程都使用了同一个key,但是只在设置了数据的当前线程可以获得这个值。换句话说,这个值只在当前线程中才有(thread specific data)。

那么,这和前面的两个有什么联系呢?
首先,都是各个线程之前不会相互影响;其次,thread-specific data是POSIX规范,因此相对于__thread来说兼容性可能更好。但是,有着相似的feature,明显__threadthread_local用起来要方便一些啊!

参考

Thread-Local
Storage class specifiers



转载请注明:Serenity » thread_local & __thread & thread specific data