为何OpenMP屏障在不同线程条件块中行为存在差异?
关于OpenMP屏障条件块导致的死锁异常现象解析
下面的代码错误地将OpenMP屏障放在了只有部分线程能进入的条件块中:
int main() { #pragma omp parallel num_threads(4) { std::cout << "Hello from thread " + std::to_string(omp_get_thread_num()) + "\n"; if(omp_get_thread_num() == 0){ #pragma omp barrier } std::cout << "Goodbye from thread " + std::to_string(omp_get_thread_num()) + "\n"; } return 0; }
第一次运行输出:
Hello from thread 0 Hello from thread 1 Hello from thread 3 Hello from thread 2 Goodbye from thread 2 Goodbye from thread 3 Goodbye from thread 1 Goodbye from thread 0 | //deadlock
按逻辑,线程0应该卡在屏障处,无法执行第二个std::cout语句,但实际它执行了该语句,最终程序陷入死锁。
修改条件后的运行结果
将判断条件改为if(omp_get_thread_num() == 1)后,输出如下:
Hello from thread 0 Hello from thread 1 Hello from thread 2 Hello from thread 3 Goodbye from thread 0 Goodbye from thread 2 Goodbye from thread 3 Goodbye from thread 1
此时线程1会等待其他线程完成后再继续执行,程序没有发生死锁。
现象原因解析
OpenMP的#pragma omp barrier要求当前并行区域内的所有线程都必须到达该屏障点,否则到达的线程会一直等待,未到达的线程若已离开并行区域,就会触发死锁。
屏障在线程0的条件块中时:
- 线程0进入条件块执行屏障,会等待其他3个线程到达屏障点,但其他线程不会进入该条件块,直接执行
Goodbye输出后退出并行区域。 - 线程0能执行
Goodbye是因为GCC的OpenMP实现中,主线程(线程0)在并行区域内遇到屏障时,若其他线程已退出并行区域,运行时库会临时唤醒主线程让其继续执行后续代码,但主线程最终会因等待已不存在的线程陷入死锁,也就是输出末尾的阻塞状态。
- 线程0进入条件块执行屏障,会等待其他3个线程到达屏障点,但其他线程不会进入该条件块,直接执行
屏障在线程1的条件块中时:
- 线程1进入条件块执行屏障,等待其他线程到达。主线程(线程0)和线程2、3执行完
Goodbye后,会触发OpenMP并行区域结尾的隐式屏障——这个隐式屏障要求所有线程必须到达并行区域末尾才能退出。 - 线程0、2、3在隐式屏障处等待线程1,而线程1在显式屏障处等待其他线程,两者形成"互相等待"的匹配状态,最终所有线程都完成各自的等待逻辑,顺利退出并行区域,因此不会死锁。
- 线程1进入条件块执行屏障,等待其他线程到达。主线程(线程0)和线程2、3执行完
编译命令:g++ -fopenmp -Wall -std=c++17 fine.cpp
内容的提问来源于stack exchange,提问作者RC1092
相关产品推荐
相关产品推荐

