【发布时间】:2019-01-02 23:13:48
【问题描述】:
我想避免并行代码中的竞争条件。问题是我的类包含几个全局变量,为了简单起见,我们只说一个 x 以及我希望并行的 for 循环。实际代码也有一个方法,它接受一个指向类的指针,在这种情况下,它本身作为它的参数,访问更多的全局变量。因此,让整个实例 threadprivate. 我正在使用 OpenMP 可能是有意义的。
一个最小的工作示例是:
#include <iostream>
#include <omp.h>
class lotswork {
public:
int x;
int f[10];
lotswork(int i = 0) { x = i; };
void addInt(int y) { x = x + y; }
void carryout(){
#pragma omp parallel for
for (int n = 0; n < 10; ++n) {
this->addInt(n);
f[n] = x;
}
for(int j=0;j<10;++j){
std::cout << " array at " << j << " = " << f[j] << std::endl;
}
std::cout << "End result = " << x << std::endl;
}
};
int main() {
lotswork production(0);
#pragma omp threadprivate(production)
production.carryout();
}
我的问题是,我该怎么做?使用关键字 threadprivate 返回以下编译器错误消息:
error: ‘production’ declared ‘threadprivate’ after first use
我认为这里的编译器问题仍然没有solved:
这让我们明白了我使用英特尔编译器的原因。 Visual Studio 2013 为 还有 g++(我的电脑上是 4.6.2,Coliru(g++ v5.2),codingground (g++ v4.9.2)) 仅允许 POD 类型(来源)。这被列为错误 近十年,仍未完全解决。视觉 给出的 Studio 错误是错误 C3057:'globalClass':动态 当前不支持“threadprivate”符号的初始化 并且 g++ 给出的错误是 error: 'globalClass' declared 首次使用后的 'threadprivate' 英特尔编译器与类一起使用。
不幸的是,我无法访问英特尔的编译器,但使用的是 GCC 8.1.0。我做了一些背景研究,发现了关于这个here 的讨论,但是十年前这条线索很冷。我问这个问题是因为有几个人对此有疑问,并通过将类指针声明为here 或提出可怕的workarounds 来解决它。后一种方法似乎被误导了,因为指针通常被声明为常量,但是当实例仍然共享时,我们有 threadprivate 指针。
尝试解决方案
我相信我可以使用private 关键字,但我不确定如何对一个类的整个实例执行此操作,尽管我更喜欢threadprivate 关键字。在第 7 章,this book 中的图 7.17 中也讨论了与上面我的 MWE 建模类似的示例,但没有解决方案。 (我非常了解比赛条件以及为什么会出现问题。)
如有必要,我可以证明没有任何额外关键字的上述程序的输出是不确定的。
另一种解决方法
我现在想到了一个解决方案,但由于某种原因,它无法编译。从线程安全和逻辑的角度来看,我的问题应该通过以下代码来解决。然而,一定有某种错误。
#include <iostream>
#include <omp.h>
class lotswork : public baseclass {
public:
int x;
int f[10];
lotswork(int i = 0) { x = i; };
void addInt(int y) { x = x + y; }
void carryout(){
//idea is to declare the instance private
#pragma omp parallel firstprivate(*this){
//here, another instance of the base class will be instantiated which is inside the parallel region and hence automatically private
baseclass<lotswork> solver;
#pragma omp for
for (int n = 0; n < 10; ++n)
{
this->addInt(n);
f[n] = x;
solver.minimize(*this,someothervariablethatisprivate);
}
} //closing the pragma omp parallel region
for(int j=0;j<10;++j){
std::cout << " array at " << j << " = " << f[j] << std::endl;
}
std::cout << "End result = " << x << std::endl;
}
};
int main() {
lotswork production(0);
#pragma omp threadprivate(production)
production.carryout();
}
因此,根据定义,这段代码应该可以解决问题,但不知何故无法编译。我如何将这段代码放在一起,以实现所需的线程安全并进行编译,同时尊重非英特尔编译器人员不能选择 threadprivate 的约束?
【问题讨论】:
-
我对这个问题进行了大量研究,似乎很多人都关心它。与其匿名且毫无帮助地投反对票,为什么不提前指出应该改进的地方?
-
不是反对者,但如果你包含一个 minimal reproducible example 会很有帮助,足以让你认为是一个独立的、正确的代码,应该可以编译但不是。
-
公平,我稍微改变了问题。
-
您能解释一下为什么要在并行区域之间保留私有变量吗?我通常可以找到解决方法,而且我几乎不需要线程私有。说明你想做什么。
-
你确定你没有混淆 C++ 中的
private对应于类层次结构中的“可见性”和 OpenMP 中的private对应于在 per-线程基础?两者都是完全正交的概念。到目前为止,我真的不明白你的问题是什么以及你想要实现的目标......