OpenMP 任务异常处理的正确方法是什么?

ast*_*bia 6 c++ openmp

当您的代码使用 OpenMP 任务时,应该如何放置 try-catch 块?是否应该在每个任务中定义一个 try-catch 块,或者单个 try-catch 块是否可以包含所有任务?换句话说,考虑这段代码:

int main()
{
  
    #pragma omp parallel
    {
        #pragma omp single
        {
            try
            {
              #pragma omp task
              { /*some code here*/ }

              #pragma omp task
              { /*some code here*/ }            
            }
            catch(Exception &e)
            {
              //I want to do something here whenever one of the tasks throws an exception
            }
        }
    }
    
}
Run Code Online (Sandbox Code Playgroud)

如果我想捕获其中一个任务抛出的任何异常,以便打印与该异常相对应的错误消息并中止程序,上面的代码是否正确?或者我是否需要在每个任务中定义一个 try-catch 块,如下所示?:

int main()
{
    
    #pragma omp parallel
    {
        #pragma omp single
        {
            #pragma omp task
            { 
              try
              {
                /*some code here*/
              }
              catch(Exception &e)
              {
                //print an error message here if there is an exception in the task and abort the program
              }  
            }

            #pragma omp task
            { 
              try
              {
                /*some code here*/
              }
              catch(Exception &e)
              {
                //print an error message here if there is an exception in the task and abort the program
              }  
            }

        }
    }
    
}
Run Code Online (Sandbox Code Playgroud)

或者我是否需要在每个任务内部有一个 try-catch 块,但也需要围绕所有任务?

Jér*_*ard 3

OpenMP 5.1 规范指出:

\n
\n

当任何线程遇到任务生成构造时,就会生成一个或多个显式任务。显式生成的任务的执行被分配给当前团队中的线程之一,具体取决于线程\xe2\x80\x99s 执行工作的可用性。因此,新任务的执行可以是立即的,也可以根据任务调度约束和线程可用性推迟到稍后执行。允许线程在任务调度点挂起当前任务区域,以便执行不同的任务。如果挂起的任务区域用于绑定任务,则最初分配的线程稍后将恢复挂起的任务区域的执行。如果挂起的任务区域用于未绑定的任务,则任何线程都可以恢复其执行。在主线程离开该区域末尾的隐式屏障之前,保证完成绑定到给定并行区域的所有显式任务。

\n
\n

该规范还指出:

\n
\n

在并行区域内执行的抛出必须导致执行在同一并行区域内恢复,并且抛出异常的同一线程必须捕获它

\n
\n

这意味着第一个代码通常不起作用,因为运行时可以稍后执行任务,甚至可以在另一个线程中执行任务(等待工作)。在您的示例中,需要在要跟踪异常的所有任务的内部代码中捕获异常。但是,这并不意味着您需要在每个任务中复制 try-catch 块。如果您需要捕获某些局部变量,您可以为其定义一个函数或带有模板的 lambda。

\n

这是一个例子:

\n
// You can add more parameter to tune the error message regarding \n// the code of the task. The error management could even be \n// specialized another lambda.\ntemplate <typename FunType>\nvoid catchExceptions(FunType lambda)\n{\n    try\n    {\n        lambda();\n    }\n    catch(Exception& e)\n    {\n        // Print an error message here if there is an exception in \n        // the task and abort the program.\n    }\n}\n\nint main()\n{\n    #pragma omp parallel\n    #pragma omp single\n    {\n        #pragma omp task\n        catchExceptions([&](){\n            /*some code here*/\n        });\n\n        #pragma omp task\n        catchExceptions([&](){\n            /*some code here*/\n        });\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n