OpenMP 标准规定当且仅当封闭区域处于活动状态时才omp_in_parallel()
返回true 。parallel
活动parallel
区域被定义为由多个线程组成的团队执行的区域。在您的情况下,您有一个非活动的并行区域,因为只有一个线程。因此omp_in_parallel()
返回false
并throw
执行。之所以出错,是因为 OpenMP 标准将异常限制在同一并行区域和线程:
throw
在一个区域内执行parallel
必须导致在同一parallel
区域内恢复执行,并且抛出异常的同一个线程必须捕获它。
这会使您的代码不一致,因为您让异常未经处理地通过并行区域。英特尔 OpenMP 运行时发出相同的错误,因此它不是特定于 GCC 的行为。
实际发生的情况是 GCC 通过将您的代码包装在一个try/catch
带有包罗万象的异常过滤器的块中来转换 OpenMP 区域:
#pragma omp parallel [child fn: main.omp_fn.0 (???)]
{
try
{
do_something ();
}
catch
{
<<<eh_filter (NULL)>>>
{
terminate ();
}
}
#pragma omp return
}
正是这个包罗万象的异常过滤器负责终止消息。
为了解决这个问题,整个try/catch
块应该在并行区域内,反之亦然:
# pragma omp parallel
{
try {
do_something();
} catch(int e) {
std::cerr<<"error: '"<<e<<"'\n"; // never gets here
}
}
(添加的附加块是为了让英特尔 C++ 编译器满意;对于 GCC 不是绝对必要的)
这error: '3'
按预期输出。
编辑:有趣的是你的异常处理程序甚至没有进入最终的二进制文件。即使给定 GCC 的默认优化级别(即使用 编译g++ -fopenmp -o prog prog.cc
),冗余消除器也能够检测到由于隐式内部异常处理程序将永远无法到达您的异常处理程序,因此您的处理程序被删除。然后编译器发现隐式终止处理程序也是多余的,因为整个进程已经有一个顶级异常处理程序执行相同的操作(调用terminate()
),因此甚至删除了隐式处理程序。最终的代码是如此精简和卑鄙 - 根本没有异常处理程序:
;; Function int main() (main, funcdef_no=970, decl_uid=20816, cgraph_uid=212)
int main() ()
{
int e;
int D.20855;
struct basic_ostream & D.20854;
struct basic_ostream & D.20853;
void * D.20852;
register int * D.20819;
<bb 2>:
omp_set_num_threads (1);
__builtin_GOMP_parallel_start (main._omp_fn.0, 0B, 0);
main._omp_fn.0 (0B);
__builtin_GOMP_parallel_end ();
D.20855_1 = 0;
// <------ See, ma', no exception handling at all :)
<L0>:
return D.20855_1;
}
;; Function <built-in> (main._omp_fn.0, funcdef_no=976, decl_uid=20857, cgraph_uid=222)
<built-in> (void * .omp_data_i)
{
<bb 2>:
do_something ();
return;
// <------ See, ma', they've nuked the implicit termination handler
}
人们会爱上-fdump-tree-all
GCC 选项。
编辑:关于如何修复的问题do_something()
- 使用omp_get_level()
而不是omp_in_parallel()
:
void do_something()
{
if(omp_get_level() == 0)
throw 3;
}
omp_get_level()
返回包含调用的区域的嵌套级别parallel
,无论它们是否处于活动状态。