使用task子句在我的OpenMP代码中导致混淆

时间:2016-08-23 09:26:39

标签: multithreading task openmp

我不熟悉在OpenMP中使用task子句,我不确定我是否正确理解了它的含义。这是我的测试代码:

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

void task(int p)
{
    printf("Thread ID: %d, task: %d\n", omp_get_thread_num(), p); 
}

#define N       5    
int main(int argc, char* argv[])  
{
    int i;
#pragma omp parallel num_threads(3)
    {   
#pragma omp single 
        {   
            for(i = 0;i < N; i++)
            {   
            #pragma omp task 
                task(i);
            }   
        }   
    }   
return 0;
}

我使用Intel 16.0编译器编译我的代码,并希望获得如下输出:

- Thread ID: 0, task: 0  
- Thread ID: 2, task: 4
- Thread ID: 2, task: 3 
- Thread ID: 0, task: 1 
- Thread ID: 1, task: 2

但是,此代码的实际输出是:

- Thread ID: 0, task: 5  
- Thread ID: 2, task: 5
- Thread ID: 2, task: 5 
- Thread ID: 0, task: 5 
- Thread ID: 1, task: 5

&#39;任务的输出:&#39;固定为5而不是0到4,这不是我预期的。任何人都可以帮我理解这个结果吗?

2 个答案:

答案 0 :(得分:0)

问题与任务构造中i的隐式数据共享有关。

如果我没记错的话,i被确定为在任务构造中共享,因为这个变量被隐含地确定为在并行中共享,但也许我错了。假设我是对的,您的代码会生成竞争条件,因为任务正在捕获&i而不是i的值。请注意,执行任务后i会按值传递(i的值可能为5)。

我的建议:如果您不确定变量的隐式数据共享,请将其始终显式化。在您的情况下,使用firstprivate(i)

注释任务

答案 1 :(得分:0)

您观察到的问题是由于可见性的变化。我在为gcc 4.8.4和icc 16.0.3编译它时重新运行了你的应用程序,我得到了以下结果(因执行而异):

<强> GCC

Thread ID: 2, task: 1
Thread ID: 2, task: 5
Thread ID: 2, task: 5
Thread ID: 1, task: 5
Thread ID: 0, task: 2

<强> ICC

Thread ID: 0, task: 5
Thread ID: 1, task: 3
Thread ID: 1, task: 5
Thread ID: 0, task: 5
Thread ID: 2, task: 5

由于变量i的可见性未在#pragma omp构造中声明,因此编译器决定i应声明为shared,这意味着任何线程完成的修改被其余的线程观察到。

在您的情况下,由于您希望打印的消息显示任务0到4,这意味着每个线程都应该有private i副本,如下面的代码所示 - 它源于你的。请注意#pragma omp parallel

中的修改
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

void task(int p)
{
  printf("Thread ID: %d, task: %d\n", omp_get_thread_num(), p);
}

#define N       5
int main(int argc, char* argv[])
{
  int i;
  #pragma omp parallel num_threads(3) private(i)
  {
    #pragma omp single
    {
      for(i = 0;i < N; i++)
      {
        #pragma omp task
        task(i);
      }
    }
  }
  return 0;
}

无论您使用何种编译器,此版本的结果为:

Thread ID: 2, task: 1
Thread ID: 2, task: 3
Thread ID: 2, task: 4
Thread ID: 1, task: 0
Thread ID: 0, task: 2

注意 private的展示位置可以进入#pragma omp parallel构造,或者如用户smateo建议的那样,它可以进入#pragma omp task(但是在后一种情况下,firstprivate在进入task构造时保持变量的值。关于它去向的实际决定取决于你的应用程序的语义。