就我所能理解的代码而言,除非数据已被复制,否则数据处理将无法继续,因此它并行执行是没有意义的 - 处理线程将浪费CPU时间等待复制线程完成并设置标志。
#pragma omp parallel num_threads(8)
{
int tid = omp_get_thread_num();
copydata(arrayofPtrs[tid]);
processingdata(arrayofPtrs[tid]);
}
如果你仍想保留原来的想法,也许如果两个拷贝和处理异步进行的,并在重复的方式,那么你就需要同步:你为什么不那么单块合并两种操作接入到使用Open MP atomic
操作标志:
#pragma omp parallel num_threads(16)
{
int tid = omp_get_thread_num();
if (tid < 8)
{
copydata(arrayofPtrs[tid]);
#pragma omp atomic write
flag[tid] = 1;//flag is an array of volatile int where its initial values are all 0.
}
else
{
for (int i = 0; i < 100000; ++i)
{
#pragma omp atomic read
int ready = flag[tid-8];
if (ready == 1)
{
processingdata(arrayofPtrs[tid-8]);
break;
}
else
Sleep(200);
}
}
}
对于大多数编译器的atomic
构建体具有的副作用是,称为变量变得易挥发。您也可以明确地更新使用flush
内存视图:
#pragma omp atomic write
flag[tid] = 1;
#pragma omp flush(flag)
的read
和write
子句仅支持OpenMP的最新版本。这个Sleep()
看起来像你使用的是Win32 API,因此可能使用MSVC,它不支持read
和write
修饰符,因为它只实现OpenMP 2.0,但代码仍应按预期进行编译和工作。
没有繁忙循环的另一种方法是使用OpenMP锁。初始化锁定阵列,在复制线程获取它们,然后让每个处理线程等待获取锁:
omp_lock_t locks[8];
for (int i = 0; i < 8; i++)
omp_init_lock(&locks[i]);
#pragma omp parallel num_threads(16)
{
int tid = omp_get_thread_num();
// Have the first 8 threads acquire the locks
if (tid < 8)
omp_set_lock(&locks[tid]);
#pragma omp barrier
// Now locks are set and processing can continue
if (tid < 8)
{
copydata(arrayofPtrs[tid]);
omp_unset_lock(&locks[tid]);
}
else
{
omp_set_lock(&locks[tid-8]);
processingdata(arrayofPtrs[tid-8]);
omp_unset_lock(&locks[tid-8]);
}
}
for (int i = 0; i < 8; i++)
omp_destroy_lock(&locks[i]);
您还可以实现POSIX信号,而不是OpenMP的锁同样使用Win32事件。这种方法的优点是,您无需在等待标志设置时显式循环。而是omp_set_lock()
调用会阻塞,直到复制线程释放它的锁。使用Win32事件,您可以使用WaitForSingleObject(hEvent, INFINITE);
等待复制线程发出信号。
是的,允许编译器移动指令的顺序。这明确是为语言添加障碍的原因 - 让程序员确保所需的顺序。在这种情况下,挥发性无助。 – 2013-04-21 00:43:40
@PeterR:我的代码中有太多的障碍我真的害怕死锁等等,顺便说一句,我添加一个volatile关键字的原因是为了防止编译器优化代码将标志数据加载到寄存器中。 – user2188453 2013-04-21 00:50:04
易失性不会做你认为它的作用。您需要使用C++ 11或c11原子或omp屏障。 volatile会禁用寄存器分配,但不会禁止编译器在设置标志后移动copydata()调用。 volatile用于访问设备寄存器,它没有并行代码。 – 2013-04-21 01:53:49