为什么thread_local不能应用于非静态数据成员以及如何实现线程局部非静态数据成员?

use*_*080 5 c++ multithreading openmp thread-local-storage c++11

为什么thread_local不能应用于非静态数据成员?这个问题的接受答案是:"制作非静态结构或类成员线程本地没有意义." 老实说,我看到很多很好的理由让非静态数据成员成为线程本地的.

假设我们有一种ComputeEngine成员函数computeSomething,它被连续多次调用.成员函数内部的一些工作可以并行完成.为此,每个线程需要某种类型ComputeHelper,例如,辅助数据结构.所以我们真正想要的是以下内容:

class ComputeEngine {
 public:
  int computeSomething(Args args) {
    int sum = 0;
    #pragma omp parallel for reduction(+:sum)
    for (int i = 0; i < MAX; ++i) {
      // ...
      helper.xxx();
      // ...
    }
    return sum;
  }
 private:
  thread_local ComputeHelper helper;
};
Run Code Online (Sandbox Code Playgroud)

不幸的是,这段代码无法编译.我们可以做的是:

class ComputeEngine {
 public:
  int computeSomething(Args args) {
    int sum = 0;
    #pragma omp parallel
    {
      ComputeHelper helper;
      #pragma omp for reduction(+:sum)
      for (int i = 0; i < MAX; ++i) {
        // ...
        helper.xxx();
        // ...
      }
    }
    return sum;
  }
};
Run Code Online (Sandbox Code Playgroud)

但是,这将构造和破坏ComputeHelper之间的连续调用computeSomething.假设构造ComputeHelper昂贵(例如,由于巨大向量的分配和初始化),我们可能希望ComputeHelper在连续调用之间重用s.这引出了以下样板方法:

class ComputeEngine {
  struct ThreadLocalStorage {
    ComputeHelper helper;
  };
 public:
  int computeSomething(Args args) {
    int sum = 0;
    #pragma omp parallel
    {
      ComputeHelper &helper = tls[omp_get_thread_num()].helper;
      #pragma omp for reduction(+:sum)
      for (int i = 0; i < MAX; ++i) {
        // ...
        helper.xxx();
        // ...
      }
    }
    return sum;
  }
 private:
  std::vector<ThreadLocalStorage> tls;
};
Run Code Online (Sandbox Code Playgroud)
  1. 为什么thread_local不能应用于非静态数据成员?这种限制背后的理由是什么?我没有给出线程局部非静态数据成员完美意义的好例子吗?
  2. 实现线程局部非静态数据成员的最佳实践是什么?

Joh*_*nck 4

至于为什么thread_local不能应用于非静态数据成员,会破坏此类成员通常的排序保证。也就是说,单个public/private/protected组内的数据成员必须按照与类声明中相同的顺序在内存中布局。更不用说如果您在堆栈上分配一个类会发生什么——TLS 成员不会进入堆栈。

至于如何解决这个问题,我建议使用boost::thread_specific_ptr. 您可以将其中之一放入您的类中并获得您想要的行为。