繁体   English   中英

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

[英]Why may thread_local not be applied to non-static data members and how to implement thread-local non-static data members?

为什么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;
};

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

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;
  }
};

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

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;
};
  1. 为什么thread_local不能应用于非静态数据成员? 这种限制背后的理由是什么? 我没有给出线程局部非静态数据成员完美意义的好例子吗?
  2. 实现线程局部非静态数据成员的最佳实践是什么?

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

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

线程本地存储通常的工作方式是在线程特定的数据结构中获得一个指针(例如Windows中的TEB)

只要所有线程局部变量都是静态的,编译器就可以轻松计算这些字段的大小,分配大小的结构并为每个字段分配一个静态偏移量。

一旦你允许非静态字段,这整个方案变得更加复杂 - 解决它的一种方法是一个额外的间接级别并在每个类中存储索引(现在你在类中隐藏字段,而不是意外)。

他们显然决定让每个应用程序根据需要处理它,而不是在实现者身上提升这种方案的复杂性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM