Multithreading 使用task子句时,我的OpenMP代码中会出现混乱的结果

Multithreading 使用task子句时,我的OpenMP代码中会出现混乱的结果,multithreading,task,openmp,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

我对在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;
}
但是,此代码的实际输出是:

- 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

“task:”的输出固定为5,而不是0到4,这不是我所期望的。有人能帮我理解这个结果吗?

这个问题与任务结构中
i
的隐式数据共享有关

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


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

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

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


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

注释任务。您观察到的问题是由于可见度可变。在为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
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
icc

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
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
声明为
共享
,这意味着任何线程所做的修改都会被剩余线程观察到

在您的例子中,由于您希望打印显示任务0到4的消息,这意味着每个线程都应该有其
i
private
副本,如下代码所示——这是您的代码派生的。请注意
#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;
}

请注意,
private
的位置可以进入
#pragma omp parallel
结构,或者按照用户smateo的建议,它可以进入
#pragma omp task
(但在后一种情况下,作为
firstprivate
以在进入
task
结构时保留变量的值)。实际的决定取决于应用程序的语义。

您观察到的问题是由于可变的可见性。在为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
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
icc

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
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
声明为
共享
,这意味着任何线程所做的修改都会被剩余线程观察到

在您的例子中,由于您希望打印显示任务0到4的消息,这意味着每个线程都应该有其
i
private
副本,如下代码所示——这是您的代码派生的。请注意
#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;
}

请注意,
private
的位置可以进入
#pragma omp parallel
结构,或者按照用户smateo的建议,它可以进入
#pragma omp task
(但在后一种情况下,作为
firstprivate
以在进入
task
结构时保留变量的值)。具体的决定取决于应用程序的语义。

我也尝试过gcc 4.4.7编译器,结果与英特尔编译器相似。我也尝试过gcc 4.4.7编译器,结果与英特尔编译器相似。我建议您接受/支持我们的任何答案?那么,我可以建议你接受/赞成我们的任何回答吗?