OpenMP 嵌套并行区域中如何识别主程序线程?
结论
在嵌套的 parallel region 中,omp_get_thread_num() == 0 只能说明当前线程是当前 team 的主线程,不能据此判断它就是进入最外层并行区域的主程序线程。
要在并行区域内识别主程序线程,可以检查当前线程在每一层并行 team 中的编号是否都为 0。OpenMP 的 omp_get_ancestor_thread_num(level) 可以查询当前线程在指定层级的祖先线程编号。
#include <omp.h>
bool is_main_program_thread_in_parallel_region()
{
const int level = omp_get_level();
// 此函数约定只在 parallel region 内调用。
if (level == 0) {
return false;
}
for (int i = 1; i <= level; ++i) {
if (omp_get_ancestor_thread_num(i) != 0) {
return false;
}
}
return true;
}
在并行区域中调用:
#pragma omp parallel num_threads(2)
{
#pragma omp parallel num_threads(2)
{
if (is_main_program_thread_in_parallel_region()) {
// 当前执行线程沿每一层并行 team 都是线程 0
}
}
}
这个判断的前提是,程序的最外层 parallel region 由主程序线程进入。只要某一层的祖先线程编号不是 0,当前执行线程就不属于从主程序线程延伸出的那条线程路径。
为什么内层线程编号为 0 不够
omp_get_thread_num() 返回当前 team 内的线程编号。每个 parallel region 都有自己的 team 和编号,因此进入内层区域后,线程编号会在新 team 中重新计算。原问题的第二个示例中,外层线程 1 启动了内层 parallel region。内层的线程 0 是该 team 的主线程,但不是主程序线程。
原示例中的 num_theads(2) 看起来是拼写错误,应为 num_threads(2);否则 OpenMP 指令无法按预期解析。
关于 std::this_thread::get_id()
可以在进入并行区域前保存 std::this_thread::get_id(),再在区域内比较当前 ID。不过,OpenMP 规范定义的是 OpenMP 线程和 team 的语义,不能仅凭 OpenMP 编号推断它与 std::thread 线程 ID 的映射关系。因此,如果目标是按照 OpenMP 语义识别主程序线程,使用 OpenMP 的层级查询更直接。如果程序还依赖 std::thread 与 OpenMP 线程之间的对应关系,应核对所用编译器和 OpenMP 运行时的支持约定。
备注:内容仅供参考。
