[英]How to synchronize the value of a variable among all threads?
如果我有以下情況:
bool cond_var;
#pragma omp parallel shared(cond_var)
{
bool some_private_var;
// ...
do {
#pragma omp single
{
cond_var = true;
}
// do something, calculate some_private_var;
// ...
#pragma omp atomic update
cond_var &= some_private_var;
// Syncing step
// (???)
} while(cond_var);
// ... (other parallel stuff)
}
我希望所有循環的do-while循環具有相同的迭代次數,但是當我嘗試#pragma omp barrier
作為同步步驟時(恰好在循環結束之前),我以死鎖結束。 打印cond_var
的值表明,一些線程將其視為true
而其他線程將其視為false
,因此對於某些線程而言,該循環已完成,而其他線程則cond_var
了障礙。 然后,我嘗試了各種組合以及barrier
和flush
排序,但是沒有運氣(對於某些組合,死鎖被推遲了)。
如何在線程之間正確組合和同步循環條件,以便所有循環具有相同的迭代次數?
UPDATE
我還嘗試使用#pragma atomic read
並將cond_var
的值加載到另一個私有變量,並測試該條件。 它也沒有用。 顯然,原子讀取保證我具有一致的值(舊值或新值),但不保證它是最新的。
更新2
根據代碼Jonathan Dursi的代碼,這是一個MVCE,看起來更像我要嘗試執行的操作:
#include <omp.h>
#include <cstdio>
#include <random>
#include <chrono>
#include <thread>
int main() {
bool cond_var;
const int nthreads = omp_get_max_threads();
#pragma omp parallel default(none) shared(cond_var)
{
bool some_private_var;
std::random_device rd;
std::mt19937 rng(rd());
unsigned iter_count = 0;
/* chance of having to end: 1 in 6**nthreads; all threads must choose 0 */
std::uniform_int_distribution<int> dice(0,5);
const int tid = omp_get_thread_num();
printf("Thread %d started.\n", tid);
do {
++iter_count;
#pragma omp once shared(cond_var)
{
// cond_var must be reset to 'true' because it is the
// neutral element of &
// For the loop to end, all threads must choose the
// same random value 0
cond_var = true;
}
some_private_var = (dice(rng) == 0);
// If all threads choose 0, cond_var will remain 'true', ending the loop
#pragma omp atomic update
cond_var &= some_private_var;
#pragma omp barrier
} while(!cond_var);
printf("Thread %d finished with %u iterations.\n", tid, iter_count);
}
return 0;
}
在具有足夠邏輯核心以同時運行所有邏輯核心的機器中以8個線程運行,大多數運行在第一次迭代中死鎖,盡管在第二次迭代中有一次運行正確完成(不符合1679616中1的機會(6 ** 8)所有線程都選擇0)。
問題是,在while循環中,您要兩次更新cond_var並第三次使用它,並且您需要確保這些操作不會相互干擾。 每次循環迭代,代碼:
因此,需要確保一個線程未將cond_var設置為true(1),而其他線程對其進行了設置(2); 使用線程進行循環外測試時,沒有線程仍在運行(2)(3); 並且沒有線程對其進行測試(3),而線程將其設置為true(1)。
這樣做的明顯方法是設置屏障,在這三種情況中的每一種之間都設置了一個屏障-因此設置了三個屏障。 所以這工作:
#include <omp.h>
#include <random>
#include <chrono>
#include <thread>
#include <iostream>
int main() {
bool cond_var;
#pragma omp parallel default(none) shared(cond_var,std::cout)
{
bool some_private_var;
std::random_device rd;
std::mt19937 rng(rd());
unsigned iter_count = 0;
std::uniform_int_distribution<int> dice(0,1);
const int tid = omp_get_thread_num();
printf("Thread %d started.\n", tid);
do {
++iter_count;
#pragma omp barrier
#pragma omp single
cond_var = true;
// implicit barrier here after the single; turned off with a nowait clause.
some_private_var = (dice(rng) == 0);
// If all threads choose 0, cond_var will remain 'true', ending the loop
#pragma omp atomic update
cond_var &= some_private_var;
#pragma omp barrier
} while(!cond_var);
#pragma omp critical
std::cout << "Thread " << tid << " finished with " << iter_count << " iterations." << std::endl;
}
return 0;
}
通過讓每個線程在共享數組中僅設置一個局部變量,然后讓一個線程來執行與操作,您可以做得更好。 因此,您仍然需要兩個障礙,一個是確保每個人都在完成安定操作之前完成,另一個是要確保在完成測試前完成安定操作:
#include <omp.h>
#include <random>
#include <chrono>
#include <thread>
#include <iostream>
int main() {
bool cond_var;
const int num_threads = omp_get_max_threads();
const unsigned int spacing=64/sizeof(bool); /* to avoid false sharing */
bool local_cond_var[num_threads*spacing];
#pragma omp parallel default(none) shared(cond_var,std::cout,local_cond_var)
{
std::random_device rd;
std::mt19937 rng(rd());
unsigned iter_count = 0;
std::uniform_int_distribution<int> dice(0,1);
const int tid = omp_get_thread_num();
printf("Thread %d started.\n", tid);
do {
++iter_count;
local_cond_var[tid*spacing] = (dice(rng) == 0);
#pragma omp barrier
#pragma omp single
{
cond_var = true;
for (int i=0; i<num_threads; i++)
cond_var &= local_cond_var[i*spacing];
}
// implicit barrier here after the single; turned off with a nowait clause.
} while(!cond_var);
#pragma omp critical
std::cout << "Thread " << tid << " finished with " << iter_count << " iterations." << std::endl;
}
return 0;
}
請注意,無論是顯式的還是隱式的,這些障礙都意味着沖刷共享變量,並向單身人士添加nowait子句會導致間歇性死鎖。
在循環體中的最后一條語句之后放置#pragma omp barrier
並不會給我造成僵局,但這也不足夠。 盡管工作線程將在屏障處等待直到它們都可以一起通過,但這不能確保它們在另一端具有一致的cond_var
視圖。 如果在任何迭代中要更新cond_var
的第一個線程cond_var
將其cond_var
為true
,那么盡管另一個線程后來將其設置為false
但這些線程中的某些或全部仍可以執行另一個迭代。 只有當這些線程返回原子更新時,他們才能確定看到其他線程寫入的值。
在測試循環條件之前,您應該能夠通過在屏障之后執行條件變量的原子讀取來解決該問題。 您需要執行此操作或采取其他措施來解決問題,因為它違反了OpenMP約束,線程組中的不同線程到達該障礙的次數不同。 實際上,這可能是程序掛起的原因:執行額外迭代的線程被卡住,等待其他線程進入障礙。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.