我有以下代码可用于我可用的编译器(xlC和gcc),但我不知道它是否完全兼容(我没有在OpenMP 3.0规范中找到明确禁止它的任何内容):
#include <iostream>
#include <vector>
#include <omp.h>
struct A {
int tid;
A() : tid(-1) { }
A(const A&) { tid = omp_get_thread_num(); }
};
int main() {
A a;
std::vector<int> v(10);
std::vector<int>::iterator it;
#pragma omp parallel for firstprivate(a)
for (it=v.begin(); it<v.end(); ++it)
*it += a.tid;
for (it=v.begin(); it<v.end(); ++it)
std::cout << *it << ' ';
std::cout << std::endl;
return 0;
}
我的动机是弄清楚omp并行中有多少个线程和每个线程的id 部分(我不希望为正在处理的每个元素调用它)。我是否有可能导致未定义的行为?
答案 0 :(得分:2)
我只是从循环中解耦(开始)并行区域,并使用私有变量来保持tid:
std::vector<int>::iterator it;
int tid;
#pragma omp parallel private(tid)
{
tid = omp_get_thread_num();
#pragma omp for
for (it=v.begin(); it<v.end(); ++it)
*it += tid;
}
补充:以下是来自the OpenMP specification(第2.9.3.4节)的引用,这些引号让我认为您的代码符合要求,因此不会产生UB (但请参见下面的其他内容):
...新的列表项是从构造之前存在的原始列表项初始化的。对于引用构造中任何语句中的列表项的每个任务,都会对新列表项的初始化执行一次。初始化在执行构造之前完成。
对于
firstprivate
或parallel
构造上的task
子句,新列表项的初始值是紧接构造之前存在的原始列表项的值遇到构造的任务区域。C / C ++:...对于类类型的变量,调用复制构造函数来执行初始化。调用类类型的不同变量的复制构造函数的顺序是未指定的。
C / C ++:出现在
firstprivate
子句中的类类型(或其数组)的变量 需要一个可访问的,明确的类类型的复制构造函数。
已添加-2:但是,未指定哪个线程执行firstprivate
变量的复制构造函数。所以从理论上讲,它可以由区域的主线程完成变量的所有副本。在这种情况下,omp_get_thread_num()
的值在所有副本中都相等,0或者在嵌套的平行区域的情况下,外部区域中的线程数。 因此,从OpenMP角度来看,它是一个定义的行为,可能会导致您的程序中出现数据竞争。
答案 1 :(得分:0)
当你遍历向量时,你应该使用它!= v.end(),而不是它&lt;鬻()。但是,在这种情况下,并行for循环不再有效。我将按以下方式重构代码的这一部分:
#pragma omp parallel for firstprivate(a)
for (int i = 0 ; i < v.size() ; i++ )
v[i] += a.tid;