C++ 如何确保多线程编程中的执行顺序?
以下多线程代码中是否存在任何问题?它总是给我不一致的结果。编译器优化可能会将标志设置行移到数据处理行之前,从而导致严重的数据争用情况 有没有办法在不增加障碍的情况下避免这种情况C++ 如何确保多线程编程中的执行顺序?,c++,c,multithreading,openmp,C++,C,Multithreading,Openmp,以下多线程代码中是否存在任何问题?它总是给我不一致的结果。编译器优化可能会将标志设置行移到数据处理行之前,从而导致严重的数据争用情况 有没有办法在不增加障碍的情况下避免这种情况 #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 volatil
#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);
};
};
#pragma omp并行num_线程(16)
int tid=omp_get_thread_num();
如果(tid您可以在处理线程的标记测试周围使用一个循环,这样它们将在标记上旋转锁,直到它被设置。但是,这部分代码看起来是连续的,那么为什么要使用多个线程进行复制/处理呢?您可以用一个线程进行复制,然后用同一个线程继续处理该块。rstand您的代码,除非数据已被复制,否则数据处理无法继续,因此并行执行没有意义-处理线程将浪费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);
}
}
}
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]);
omp__t locks[8];
对于(int i=0;i<8;i++)
omp_init_lock(&locks[i]);
#pragma omp并行num_线程(16)
{
int tid=omp_get_thread_num();
//让前8个线程获得锁
如果(tid<8)
omp_set_lock(&locks[tid]);
#布拉格奥姆普屏障
//现在已设置锁,处理可以继续
如果(tid<8)
{
copydata(arrayofPtrs[tid]);
omp_unset_lock(&locks[tid]);
}
其他的
{
omp_set_lock(&locks[tid-8]);
处理数据(arrayofPtrs[tid-8]);
omp_unset_lock(&locks[tid-8]);
}
}
对于(int i=0;i<8;i++)
omp_destroy_lock(&locks[i]);
您还可以使用POSIX信号量的Win32事件而不是OpenMP锁来实现相同的功能。这种方法的优点是,在等待设置标志时不需要显式循环。而是使用omp\u set\u lock()
调用将被阻止,直到复制线程释放其锁。对于Win32事件,您可以使用WaitForSingleObject(hEvent,INFINITE)
等待复制线程发出信号。是的,编译器可以移动指令的顺序。这就是在语言中添加障碍的明显原因-允许程序员确保所需的顺序。Volatile在这种情况下没有帮助。@PeterR:我的代码中已经有太多障碍了,我真的很抱歉顺便说一句,我之所以添加volatile关键字是为了防止编译器优化代码以将标志数据加载到寄存器中。volatile不做您认为它做的事情。您需要使用c++11或c11原子或omp屏障。volatile禁用寄存器分配,但不禁止编译器移动t他复制数据设置标志后调用。volatile用于访问设备寄存器,它在并行代码中没有位置。Michael编辑了我的线程,很糟糕一些信息被编辑掉了,实际上这段代码只是显示了algorthim的概念,而不是真正的代码,这段代码很长,并且做了很多其他事情。不过,我不明白为什么你想在不同的线程中进行复制和处理。但只要你只设置了一个方向的标志形式,你就可以使用忙等待。我没有碰你的代码@user2188453;我只是复制编辑过的你的散文。你可以查看编辑历史,看看我做了什么-单击“编辑的X分钟前”链接。
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]);