下面的多线程代码有什么问题吗?它总是给我不一致的结果。看起来编译器优化可能会将标志设置行移动到数据处理行之前,这会导致严重的数据竞争情况。
有没有办法在不增加障碍的情况下避免这种情况?
#pragma omp parallel num_threads(16)
int tid=omp_get_thread_num();
if (tid<8)
{
copydata(arrayofPtrs[tid]);
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)
{
if (flag[tid-8]==1)
{
processingdata(arrayofPtrs[tid-8]);
break;
}
else
Sleep(200);
};
};发布于 2013-04-21 08:44:23
您可以对处理线程的标志测试使用循环,以便它们在标志上旋转锁,直到它被设置。然而,这部分代码看起来是连续的,那么为什么要使用多个线程进行复制/处理呢?您可以使用线程进行复制,然后使用相同的线程继续处理该块。
发布于 2013-04-21 15:51:14
就我所理解的代码而言,除非数据被复制,否则数据处理将无法继续,因此并行处理没有意义-处理线程将浪费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)仅在最新的OpenMP版本上支持read和write子句。这个Sleep()看起来像是在使用MSVC应用程序接口,因此可能使用的是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);等待复制线程发出信号。
https://stackoverflow.com/questions/16126778
复制相似问题