JavaScript is required to for searching.
跳过导航链接
退出打印视图
Oracle Solaris Studio 12.3:OpenMP API 用户指南     Oracle Solaris Studio 12.3 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  OpenMP API 简介

2.  编译并运行 OpenMP 程序

3.  实现定义的行为

4.  嵌套并行操作

5.  任务处理

5.1 任务处理模型

5.2 数据环境

5.3 任务处理示例

5.4 编程注意事项

5.4.1 THREADPRIVATE 和线程特定的信息

5.4.2 锁

5.4.3 对堆栈数据的引用

6.  自动确定变量的作用域

7.  作用域检查

8.  性能注意事项

A.  子句在指令中的放置

索引

5.4 编程注意事项

任务处理功能使 OpenMP 程序的复杂性有所增加。程序员需要特别注意带有任务的程序的工作原理。本节讨论一些要考虑的编程问题。

5.4.1 THREADPRIVATE 和线程特定的信息

当线程遇到任务调度点时,实现可能会选择暂停当前任务并安排线程处理另一个任务。该行为意味着 threadprivate 变量的值或线程特定的其他信息(如线程数)可能会在任务调度点发生变化。

如果暂停的任务为绑定 (tied) 任务,则恢复执行该任务的线程与暂停该任务的线程将是同一线程。因此,恢复该任务后,线程数将保持相同。但是,threadprivate 变量的值可能会更改,原因是可能会安排线程处理另一个任务,这样会在恢复暂停的任务之前修改 threadprivate 变量。

如果暂停的任务为非绑定 (untied) 任务,则恢复执行该任务的线程可能与暂停该任务的线程不同。因此,线程数和 threadprivate 变量的值在任务调度点之前和之后都可能不同。

5.4.2 锁

OpenMP 指定,锁不再归线程所有,而是归任务所有。一旦获取了锁,当前任务就会拥有该锁,同一任务必须先释放锁才能完成任务。

另一方面,critical 构造仍保留采用基于线程的互斥机制

使用锁时,需要格外小心锁所有权的变化。以下示例(在 OpenMP 3.1 规范的附录 A 中出现)符合 OpenMP 2.5,因为在并行区域中释放锁 lck 的线程与在该程序顺序部分中获取锁的线程为同一线程。并行区域的主线程与初始线程相同。但是,该示例不符合 OpenMP 3.1,因为释放锁 lck 的任务区域与获取锁的任务区域不同。

示例 5-2 使用锁的示例:不符合 OpenMP 3.0

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

int main()
{
  int x;
  omp_lock_t lck;

  omp_init_lock (&lck);
  omp_set_lock (&lck);
  x = 0;

  #pragma omp parallel shared (x)
  {
    #pragma omp master
    {
      x = x + 1;
      omp_unset_lock (&lck);
    }
  }
  omp_destroy_lock (&lck);
}

5.4.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 副本,该副本(在某些实现中,包括 Oracle 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);
 }