我的资源需要在两次访问之间保持其状态。使用OpenMP将程序并行化时,我想确保每个线程都有自己的副本,并且实例不会被破坏并为每个并行区域重新创建。为此,我使用了一个全局变量threadprivate
。下面,我有一个简单的测试用例,应说明设置。
我有两个问题:
是否保证在程序执行期间每个线程仅创建一次资源(在obj下面)?
当我在四个线程上运行示例程序时,每个线程报告“ Obj created ...”和“ State set to ...”,但只有线程零报告“ Obj destroy ...”。这里发生了什么?
#ifdef _OPENMP
#include <omp.h>
#endif
#include <vector>
#include <iostream>
#include <iomanip>
class obj {
public:
obj() : state(0) {
res = new int [100];
#pragma omp critical
{
std::cout << "Obj created, state " << state;
#ifdef _OPENMP
std::cout << ", thread " << omp_get_thread_num();
#endif
std::cout << std::endl;
}
}
~obj() {
delete[] res;
#pragma omp critical
{
std::cout << "Obj destroyed, state " << state;
#ifdef _OPENMP
std::cout << ", thread " << omp_get_thread_num();
#endif
std::cout << std::endl;
}
}
void init(int set) {
state = set;
#pragma omp critical
{
std::cout << "State set to " << state;
#ifdef _OPENMP
std::cout << ", thread " << omp_get_thread_num();
#endif
std::cout << std::endl;
}
}
int operator()() {
return ++state;
}
private:
int state;
int* res;
};
extern obj obj1;
#pragma omp threadprivate(obj1)
obj obj1;
void init() {
#ifdef _OPENMP
#pragma omp parallel
{
obj1.init(100 * omp_get_thread_num());
}
#else
obj1.init(100);
#endif
}
void work() {
std::cout << "Computing" << std::endl;
int constexpr length = 20;
std::vector<int> vec(length);
#pragma omp parallel for
for (int idx = 0; idx < length; idx++) {
vec[idx] = obj1();
}
std::cout.fill('0');
for (auto const & e: vec) {
std::cout << std::setw(3) << e << ' ';
}
std::cout << std::endl;
}
int main() {
init();
work();
work();
work();
}
最佳答案
在以下情况下,threadprivate可以正常工作
每次声明变量后都出现#pragma omp threadprivate
;
必须使用omp_set_dynamic(false)
关闭动态线程(默认为实现定义)。
请参见示例here。
我完全不会依赖析构函数。 OpenMP有很多未指定的地方,编译器可能会对其进行优化。
这是OpenMP Spec(v4.0 p.12.14.2)的摘录
根据基本语言如何处理静态变量,但在程序中未指定的位置,释放了线程私有变量的所有副本的存储。
和
未指定用于类类型的不同线程专用C ++变量的任何析构函数的调用顺序。threadprivate
的更多信息
(v4.0 p2.4.12)
threadprivate
变量的每个副本都按照程序指定的方式初始化一次,但是在第一次引用该副本之前在程序中的未指定点初始化。根据基本语言处理静态变量的方式,释放了threadprivate
变量的所有副本的存储,但是在程序中未指定的位置。
线程引用另一个线程的threadprivate
变量副本的程序不合格。
如果正在执行的线程切换到另一个修改变量的任务,则threadprivate
变量的内容可以在任务调度点之间变化。有关任务计划的更多详细信息,请参阅第14页的第1.3节和第113页的2.11节。
在parallel
区域中,主线程将引用该线程中遇到parallel
区域的变量的副本。
在顺序零件过程中,将引用初始线程的变量副本。保证初始线程的threadprivate
变量副本中的数据值在程序中对变量的两个连续引用之间保持不变。
仅在满足以下所有条件的情况下,才能确保非初始线程的threadprivate
变量中的数据值在两个连续的活动parallel
区域之间持久存在:
两个parallel
区域都不嵌套在另一个显式的parallel
区域内。
用于执行两个parallel
区域的线程数是相同的。
用于执行两个parallel
区域的线程相似性策略是相同的。
封闭的任务区域中dyn-var内部控制变量的值在进入两个parallel
区域时为false。
如果所有这些条件都成立,并且在两个区域中均引用了threadprivate
变量,则在各自区域中具有相同线程号的线程将引用该变量的相同副本。