【问题标题】:Which value does e take here?e 在这里取什么值?
【发布时间】:2026-01-14 10:35:01
【问题描述】:

我有以下一段代码(注意:这是为了帮助我理解这个概念,所以它是一个示例代码,而不是我打算运行的东西)

List ml; //my_list
Element *e;
#pragma omp parallel
#pragma omp single
{
    for(e=ml->first;e;e=e->next)
    #pragma omp task 
    process(e);//random function
}

有人提到 e 将始终具有相同的值,我试图思考为什么会这样以及值将是什么。

我的尝试/推理:

e 的值在pragma omp single 内部发生变化,这些变化不会在任务内部执行(如果我没记错的话,除非我使用类似 firstprivate(e) 的东西,否则单个区域的值不会被带到任务内部,此外,它将采用的值将是随机的,因为我们没有将 e 初始化为单个和并行 omp 区域之外的任何变量,并且 e 将作为一个值,如果我们已经在外部初始化为一个值 x 例如然后e 永远是 x

任何帮助纠正或验证我的推理将不胜感激。

【问题讨论】:

    标签: c multithreading openmp


    【解决方案1】:

    我在您的示例中添加了一些 cmets,希望使行为更加清晰。

    List ml; //my_list
    Element *e;
    
    #pragma omp parallel  // e is shared for the parallel region
    #pragma omp single
    {
       for(e=ml->first;e;e=e->next)
         #pragma omp task  // since e is shared, all tasks will see the "same" e
         process(e);
    }
    

    会发生什么(由上面的 cmets 表示):您在 parallel 构造的范围之外声明了 e。根据 OpenMP 规范,该变量将在所有正在执行的线程之间共享。然后,single 构造将执行限制为团队的任何一个线程(e 仍然在所有线程之间共享,请参阅https://www.openmp.org/spec-html/5.1/openmpsu113.html#x148-1600002.21.1)。

    当挑选线程遇到 task construct @ construct时,OpenMP规范要求从 task继承创建的任务,继承 e variable(共享)的共享属性并且选择的线程在执行for 循环时可能会覆盖e 变量。

    这就是firstprivate(e) 的用武之地:

    List ml; //my_list
    Element *e;
    
    #pragma omp parallel  // e is shared for the parallel region
    #pragma omp single
    {
      for(e=ml->first;e;e=e->next)
      #pragma omp task firstprivate(e) // task now receives a private "copy" of e
        process(e);
    }
    

    在这里,创建任务将具有 e的私人副本,该副本以 @ 98765436 @的当前值进行初始化,因为挑选的线程通过 for loop。

    解决此问题的另一种方法是:

    List ml; //my_list
    
    #pragma omp parallel  
    #pragma omp single
    {
      Element *e;   // e is thread-private inside the parallel region
      for(e=ml->first;e;e=e->next)
      #pragma omp task // task now receives a private "copy" of e w/o firstprivate
        process(e);
    }
    

    由于在此示例中,OpenMP 规范要求应将变量视为您指定了firstprivate(e)(请参阅https://www.openmp.org/spec-html/5.1/openmpsu113.html#x148-1610002.21.1.1)。

    【讨论】:

    • 要清楚,对 e 所做的任何修改都不会对任务“可见”,因为它只需要全局变量初始化?
    • 取决于您如何进行更改。如果您使用firstprivate(e)(或将e 的定义移动到parallel 构造中),该任务将获得一个与共享e 断开连接的副本,然后共享e 可以独立,但不会更改副本e 在任务中。
    • 在您的原始代码中,e 将被 for 循环的每次迭代覆盖,并且所有之前创建的任务都将看到这些更改。