就我所能理解的代碼而言,除非數據已被複制,否則數據處理將無法繼續,因此它並行執行是沒有意義的 - 處理線程將浪費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