为什么thread_local不能应用于非静态数据成员以及如何实现线程本地非静态数据成员? [英] Why may thread_local not be applied to non-static data members and how to implement thread-local non-static data members?

查看:287
本文介绍了为什么thread_local不能应用于非静态数据成员以及如何实现线程本地非静态数据成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



假设我们有一些 ComputeEngine 具有连续调用多次的成员函数 computeSomething 。成员函数内的一些工作可以并行完成。为此,每个线程需要某种提供例如辅助数据结构的 ComputeHelper 。所以我们实际想要的是以下:

 类ComputeEngine {
public:
int computeSomething args){
int sum = 0;
#pragma omp parallel for reduction(+:sum)
for(int i = 0; i // ...
helper .xxx();
// ...
}
return sum;
}
private:
thread_local ComputeHelper helper;
};

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

  ComputeEngine类{
public:
int computeSomething ){
int sum = 0;
#pragma omp parallel
{
ComputeHelper helper;
#pragma omp for reduction(+:sum)
for(int i = 0; i // ...
helper。 xxx();
// ...
}
}
return sum;
}
};然而,这将构造和破坏 ComputeHelper computeSomething 的连续调用之间。假设构造 ComputeHelper 是昂贵的(例如,由于分配和初始化巨大向量),我们可能想重用 ComputeHelper s在连续调用之间。这导致我有以下样板方法:

 类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 // ...
helper。 xxx();
// ...
}
}
return sum;
}
private:
std :: vector< ThreadLocalStorage> tls;
};




  1. 为什么 thread_local 不应用于非静态数据成员?什么
    是这种限制背后的理由?我有没有给出一个好的
    示例,其中线程本地非静态数据成员完美的
    意义?

  2. static
    data members?

    解决方案

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



    至于如何解决这个问题,我建议使用 boost :: thread_specific_ptr 。你可以把其中一个内部你的类,并得到你想要的行为。


    Why may thread_local not be applied to non-static data members? The accepted answer to this question says: "There is no point in making non-static structure or class members thread-local." Honestly, I see many good reasons to make non-static data members thread-local.

    Assume we have some kind of ComputeEngine with a member function computeSomething that is called many times in succession. Some of the work inside the member function can be done in parallel. To do so, each thread needs some kind of ComputeHelper that provides, for example, auxiliary data structures. So what we actually want is the following:

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

    Unfortunately, this code will not compile. What we could do instead is this:

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

    However, this will construct and destruct the ComputeHelper between successive calls of computeSomething. Assuming that constructing the ComputeHelper is expensive (for example, due to the allocation und initialization of huge vectors), we may want to reuse the ComputeHelpers between successive calls. This leads me to the following boilerplate approach:

    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. Why may thread_local not be applied to non-static data members? What is the rationale behind this restriction? Have I not given a good example where thread-local non-static data members make perfect sense?
    2. What are best practices to implement thread-local non-static data members?

    解决方案

    As for why thread_local cannot be applied to non-static data members, it would disrupt the usual ordering guarantee of such members. That is, data members within a single public/private/protected group must be laid out in memory in the same order as in the class declaration. Not to mention what happens if you allocate a class on the stack--the TLS members would not go on the stack.

    As for how to work around this, I suggest using boost::thread_specific_ptr. You can put one of these inside your class and get the behavior you want.

    这篇关于为什么thread_local不能应用于非静态数据成员以及如何实现线程本地非静态数据成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆