据我了解您的代码,除非数据已被复制,否则数据处理无法继续,因此并行执行是没有意义的 - 处理线程将浪费 CPU 时间等待复制线程完成并设置旗帜。为什么不将这两个操作合并到一个块中:
#pragma omp parallel num_threads(8)
{
int tid = omp_get_thread_num();
copydata(arrayofPtrs[tid]);
processingdata(arrayofPtrs[tid]);
}
如果您仍想保留最初的想法,可能如果复制和处理都异步且以重复的方式发生,那么您需要使用 Open MPatomic
操作同步对标志的访问:
#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
andwrite
子句仅在最近的 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 信号量的 Win32 事件而不是 OpenMP 锁来实现相同的功能。这种方法的优点是您在等待设置标志时不需要显式循环。相反,omp_set_lock()
调用将阻塞,直到复制线程释放它的锁。使用 Win32 事件,您可以WaitForSingleObject(hEvent, INFINITE);
用来等待复制线程发出信号。