我目前正在研究一位使用OpenMp的前同事编写的代码。然而,我自己对OpenMp没有任何考验,虽然我只是通过阅读他的代码来理解基础知识,但我目前仍在努力弄清楚如何为我自己的修改声明一个threadlocal成员。
非常简化版本中的当前代码如下所示:
struct Worker
{
void work() { //... }
};
-------------------------------------------------------------------
Worker worker;
#pragma omp parallel for
for (int i = 0; i < n; ++i)
{
worker.work();
}
我想要实现的是以与此类似的方式修改Worker
类:
struct Worker
{
void work() { // m_var is accessed here }
int m_var; // should be threadlocal
};
但是我不知道如何使用OpenMP实现这一目标。请注意,Worker
中的所有其他成员不应该是同步的,也不应该是threadlocal。
PS:对于那些好奇的人来说,Worker
实际上是一个下载一些复杂内容的类,而在for循环中则执行单个下载。 m_var
将成为持有会话的对象。
答案 0 :(得分:3)
非静态数据成员在类的每个实例中都有单独的实例,并且不能是线程本地的 - 它们继承给定类的具体对象的共享类。例如,如果在OpenMP线程的堆栈上创建了类Worker
的对象(即该对象具有自动存储类),则该对象本身对该线程是私有的,worker.m_var
也是私人的。如果在堆上创建对象,则可以与其他线程共享,然后也将共享worker->m_var
。
Thread-private只能应用于具有静态存储类的数据成员:
struct Worker
{
void work();
static int m_var;
#pragma omp threadprivate(m_var)
};
int Worker::m_var;
在这种情况下,只存在Worker::m_var
的一个静态(全局)副本,并使其成为线程私有,为每个线程提供在该线程中Worker
的所有实例之间共享的单独实例。
另请注意,private
和firstprivate
无法应用于班级数据成员,无论他们是否为静态 - 请参阅this answer。
答案 1 :(得分:0)
如果static
要求对于您的情况不可接受,则可以用自己的类替换int
字段-该类可以是您的Worker
专用的,或者您可以将其作为模板,以供不同类中的不同字段重用。
无论哪种方式,只要有OpenMP线程(我们将其称为int_array
),新类的构造函数都会分配一个数组:
ThreadedInt() {
int_array = new int[omp_get_max_threads()];
}
您还将实现将这个新类转换为原始类型(在您的示例中为int
时所必需的 operators )。例如:
operator int() const {
return int_array[omp_get_thread_num()];
}
以及其他一些内容,例如作业:
int& operator = (int value) {
return int_array[omp_get_thread_num()] = value;
}
然后,其余代码可以保持不变,无论是否为OpenMP。