Visual Studio 2017中多线程调用omp_set_num_threads不同值的异常问题
问题根源
omp_set_num_threads是进程级全局设置,所有OpenMP并行区域都会共享这个值。当两个独立的操作系统线程先后或同时修改这个值时,后调用的设置会覆盖前一个,导致其中一个线程的并行区域执行时使用了错误的线程数。
解决办法
以下是几种无需统一全局线程数的可行方案:
1. 使用num_threads子句(推荐)
直接在#pragma omp指令中通过num_threads子句指定当前并行区域的线程数,完全绕过全局设置,从根源避免冲突:
线程1修改后代码:
#pragma omp parallel for num_threads(20) for(...
线程2修改后代码:
#pragma omp parallel num_threads(18) { int ith = omp_get_thread_num(); if(ith >= 18) { TRACE("ith %d, actual number of threads %d\n", ith, omp_get_num_threads()) } }
这种方式为每个并行区域单独指定线程数,互不干扰,代码简洁且可靠。
2. 线程局部存储+临时切换全局设置
如果必须保留omp_set_num_threads的调用方式,可以通过线程局部存储维护每个操作系统线程的线程数配置,在进入并行区域前临时修改全局设置,执行完成后恢复原值:
// 线程局部变量,存储当前OS线程需要的OpenMP线程数 __declspec(thread) int local_omp_threads = 0; // 线程1逻辑 local_omp_threads = 20; int old_thread_count = omp_get_max_threads(); omp_set_num_threads(local_omp_threads); #pragma omp parallel for for(... // 恢复全局设置 omp_set_num_threads(old_thread_count); // 线程2逻辑 local_omp_threads = 18; int old_thread_count2 = omp_get_max_threads(); omp_set_num_threads(local_omp_threads); #pragma omp parallel { int ith = omp_get_thread_num(); if(ith >= 18) { TRACE("ith %d, actual number of threads %d\n", ith, omp_get_num_threads()) } } omp_set_num_threads(old_thread_count2);
注意:需要用互斥锁保护全局设置的修改和恢复操作,避免两个OS线程同时操作导致的竞态问题。
3. OpenMP 4.0+细粒度线程控制(复杂场景可选)
VS2017支持OpenMP 4.0标准,可结合omp_get_thread_limit、线程亲和性等特性实现更细粒度的线程控制,但复杂度远高于num_threads子句,仅适合特殊动态调整场景。
总结
优先选择num_threads子句方案,它直接隔离不同并行区域的线程数设置,无竞态风险,代码可读性也更强。
内容的提问来源于stack exchange,提问作者Nicola Papp
相关产品推荐
相关产品推荐

