Oracle Solaris Studio 12.2:OpenMP API 用户指南

5.5.3 对栈数据的引用

任务可能会引用任务构造所在的例程的栈数据。由于任务的执行可能会延迟,直至下一个隐式或显式屏障,所以有可能出现这样的情况:给定的任务将在任务所在的例程的栈已经弹出,且栈数据被覆写(从而销毁由任务列为共享的栈数据)之后执行。

程序员应负责插入所需的同步,以确保任务引用变量时这些变量仍在栈中。以下是两个示例。

在第一个示例中,在 task 构造中将 i 指定为 shared,任务会访问在 work() 的栈中分配的 i 的副本。

任务的执行可能会延迟,使得任务将在 work() 例程已返回后,在 main() 中的并行区域末尾的隐式屏障处执行。因此当任务引用 i 时,会访问当时碰巧在栈中的某个不确定的值。

为了得到正确的结果,程序员需要确保 work() 不会在任务完成前退出。这可以通过在 task 构造之后插入 taskwait 指令来实现。或者,可以在 task 构造中将 i 指定为 firstprivate 而不是 shared


示例 5–3 栈数据:第一个示例-不正确的版本


#include <stdio.h>
#include <omp.h>
void work()
 {
   int i;

   i = 10;
   #pragma omp task shared(i)
   {
     #pragma omp critical
     printf("In Task, i = %d\n",i);
   }
 }

int main(int argc, char** argv)
 {
    omp_set_num_threads(8);
    omp_set_dynamic(0);

    #pragma omp parallel 
    {
      work();
    }
 }


示例 5–4 栈数据:第一个示例-更正的版本


#include <stdio.h>
#include <omp.h>

void work()
 {
   int i;

   i = 10;
   #pragma omp task shared(i)
   {
     #pragma omp critical
     printf("In Task, i = %d\n",i);
   }

   /* Use TASKWAIT for synchronization. */
   #pragma omp taskwait
 }

int main(int argc, char** argv)
 {
    omp_set_num_threads(8);
    omp_set_dynamic(0);

    #pragma omp parallel 
    {
      work();
    }
 }

在第二个示例中,task 构造中的 j 引用 sections 构造中的 j。因此,任务会访问 sections 构造中 jfirstprivate 副本,该副本(在某些实现中,包括 Solaris Studio 编译器)是 sections 构造的概要例程的栈中的局部变量。

任务的执行可能会延迟,使得任务将在 sections 构造的概要例程退出后,在 sections 区域末尾的隐式屏障处执行。因此当任务引用 j 时,会访问栈中的某个不确定的值。

为了得到正确的结果,程序员需要确保任务在 sections 区域达到其隐式屏障前执行。这可以通过在 task 构造之后插入 taskwait 指令来实现。或者,可以在 task 构造中将 j 指定为 firstprivate 而不是 shared


示例 5–5 第二个示例-不正确的版本


#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv)
 {
    omp_set_num_threads(2);
    omp_set_dynamic(0);
    int j=100;

    #pragma omp parallel shared(j)
    {
       #pragma omp sections firstprivate(j)
       {
          #pragma omp section
          {
             #pragma omp task shared(j)
             {
               #pragma omp critical
               printf("In Task, j = %d\n",j);
             }
          }
       }
    }

    printf("After parallel, j = %d\n",j);
 }


示例 5–6 第二个示例-更正的版本


#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv)
 {
    omp_set_num_threads(2);
    omp_set_dynamic(0);
    int j=100;

    #pragma omp parallel shared(j)
    {
       #pragma omp sections firstprivate(j)
       {
          #pragma omp section
          {
             #pragma omp task shared(j)
             {
               #pragma omp critical
               printf("In Task, j = %d\n",j);
             }

             /* Use TASKWAIT for synchronization. */
             #pragma omp taskwait
          }
       }
    }

    printf("After parallel, j = %d\n",j);
 }