OpenMP线程同步疑问:#pragma omp barrier能否保证线程同步?
首先得明确:#pragma omp barrier本身就是OpenMP里实现线程同步的核心指令之一,它的作用绝对是强制所有参与当前并行区域的线程,必须全部抵达这个barrier点后,才能继续执行barrier之后的代码。你觉得它没实现“真正同步”,大概率是对barrier的作用场景理解有偏差。
先看你给出的测试输出:
0: func() size: 64 Time: 0.000414 Start: 1522116688.801262 End: 1522116688.801676
1: func() size: 64 Time: 0.000828 Start: 1522116688.801263 End: 1522116688.802091
这里的时间差异,是线程在barrier之前执行func()的耗时不同——这完全是正常现象!线程调度由操作系统控制,每个线程的执行时机、资源分配都可能有差异,导致相同任务的执行时间不一样。而barrier的作用是:当线程0跑完func()到达barrier时,它会进入等待状态,直到线程1也完成func()并抵达barrier,此时两个线程才会同时开始执行barrier之后的代码。
举个正确的用法例子帮你理解:
#include <omp.h> #include <stdio.h> void func(int tid, int size) { // 模拟耗时任务 for (int i = 0; i < size * 10000; i++); } int main() { #pragma omp parallel num_threads(2) { int tid = omp_get_thread_num(); // 线程各自执行前置任务,耗时可能不同 func(tid, 64); // 同步点:所有线程必须完成上面的func,才能继续 #pragma omp barrier // 这里的代码,所有线程都会在barrier之后才开始执行 double after_barrier_time = omp_get_wtime(); printf("Thread %d passed barrier at: %f\n", tid, after_barrier_time); } return 0; }
运行这个代码你会发现,两个线程输出的after_barrier_time会非常接近——这就是barrier同步的效果,它保证了所有线程都完成前置工作后,才一起进入后续流程。
如果你想要的是让所有线程同时开始执行func()(也就是让任务的启动时间同步),那应该把barrier放在任务执行之前,比如:
#pragma omp parallel num_threads(2) { int tid = omp_get_thread_num(); // 先汇合,确保所有线程都准备好 #pragma omp barrier double start = omp_get_wtime(); func(tid, 64); double end = omp_get_wtime(); printf("%d: func() size: 64 Time: %f Start: %f End: %f\n", tid, end-start, start, end); }
这样修改后,两个线程的start时间会几乎一致,任务的启动就同步了。
总结一下:
#pragma omp barrier是可靠的同步指令,不存在“仅保证线程执行完成,无法实现真正同步”的问题;- 你看到的测试差异是前置任务的执行耗时不同,属于正常的线程调度现象;
- 根据你的需求调整barrier的位置:要等所有线程完成任务后再继续,就把barrier放在任务之后;要让所有线程同时启动任务,就把barrier放在任务之前。
内容的提问来源于stack exchange,提问作者bad code

