我想避免并行代码中的竞争条件。问题是我的类包含多个全局变量,为简单起见,我们只说一个x
,而我希望并行执行一个for
循环。实际的代码还具有一种方法,该方法将指向类的指针(在本例中为自身)作为其参数,以访问更多的全局变量。因此,使整个实例threadprivate.
我正在使用OpenMP可能很有意义。 MWE是
#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 ++给出的错误是错误:声明了'globalClass' 首次使用后为“ threadprivate” Intel编译器可用于类。
不幸的是,我无法访问Intel的编译器,但使用GCC 8.1.0。我做了一些背景研究,并发现了关于此here的讨论,但是十年前,这条路一直很冷。我问这个问题是因为有几个人对此有疑问,并通过将类指针声明为here或提出了可怕的workarounds来解决了这个问题。后一种方法似乎是错误的,因为通常将指针声明为常量,但是当实例仍然共享时,我们就有threadprivate
指针。
我相信我可以使用private
关键字,但是尽管我更喜欢threadprivate
关键字,但不确定如何在整个类实例中执行此操作。 this book的第7章,图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();
}
因此,基于定义的这段代码应该可以解决问题,但无法以某种方式编译。有人可以帮助我将这段代码放在一起,以实现所需的线程安全性和编译,同时遵守非Intel编译器人员无法选择threadprivate的约束吗?提前非常感谢您的帮助。
答案 0 :(得分:4)
这是长期缺少的GCC功能:
在当前的GCC版本中,thread_local
可以正常运行,
int main() {
thread_local lotswork production(0);
production.carryout();
}
但是,我认为这不适用于您的情况,因为carryout
中的并行循环仍将在单个lotswork
实例上运行。我相信这也将适用于使用threadprivate
的原始代码。您可能需要将并行循环移出carryout
成员函数之外。
答案 1 :(得分:4)
这里似乎对OpenMP构造有些困惑。 threadprivate
与thread_local一样,用于为静态生存期的对象(全局变量或静态变量)创建每个线程的副本。如前所述,这存在一些实现问题,但是即使实现可以处理该类,在非静态局部变量上使用threadprivate
也会产生错误。
对于错误,很难说没有输出,但是可能有多种原因:
{
放在编译指示行的末尾不会打开一个块,它必须在下一行。如果需要在每个线程中创建封闭类的私有副本,则可以通过将类复制构造为在并行区域内声明的变量来实现:
#pragma omp parallel
{
lotswork tmp(*this);
// do things with private version
}
但是请注意,整个事情都是私有的,因此这意味着原始副本中的f
不会被更新,除非您先对私有副本执行addInt
等效项,然后再执行{{1} }原始作业。
编辑:我最初提到使用f[n]
子句,但是
default子句仅为FORTRAN提供private和first private。为了得到
在c ++中具有相同的效果,请执行上述操作并将构造复制到每个实例的新实例中,
或使用默认情况下按值捕获的lambda,然后先对其进行私有default(firstprivate)
需要c ++ 17才能正常工作,但确实满足要求:
*this