openmp c++ 中并行块内 lambda 函数的奇怪行为

Strange behaviour of lambda function inside parallel block in openmp c++

本文关键字:函数 lambda c++ 并行 openmp      更新时间:2023-10-16

我开始学习OpenMP已经有几天了。我遇到了这个无法解决的错误。 我定义了一个 lambda 函数f它捕获局部变量s。现在,如果我在并行 for 循环中更改变量s(每个线程的私有变量(,则函数f不会反映其输出的变化,并且始终给出0.我可以理解我在范围界定方面犯了一些错误,但找不到错误。

#include <iostream>
#include <omp.h>
int main(int argc, char *argv[])
{
using namespace std;
double s;
auto f =[&]{return s;};
#pragma omp parallel for private(s)
for(int i = 0; i < 4 ; i++)
{
s = 5+i;
double a1 = f();
#pragma omp critical
cout << a1 << endl;
}
return 0;
}

如果我在并行 for 循环中定义 lambda,它实际上有效并返回s而不是0的正确值,即

#pragma omp parallel for private(s)
for(int i = 0; i < 4 ; i++)
{
s = 5+i;
double a1 = [&]{return s;}();
#pragma omp critical
cout << a1 << endl;
}

我的猜测是 lambda 函数和捕获的变量需要在同一范围内。如果在并行块外部定义了 lambda 函数,如何解决此问题?

您正在处理多个s变量。sparallel region中的变量和外部作用域中声明的s变量。

private指令声明数据在每个线程的内存中具有单独的副本。因此,每个线程都有自己的s副本,该副本驻留在与外部作用域double s;中声明的s不同的内存位置。

当您更改parallel regions的值时,您只会更改s的线程本地副本。

外部作用域(通过lambda中的ref捕获(中s的值不会更改。外部s位于不同的内存位置。

旁注,您应该初始化外部s变量:double s{ 0 };

// This value of x will remain unchanged.
int x = 5;
#pragma omp parallel private( x )
{
// What happens in the `parallel block` stays in the
// in the `parallel block`. In other words, incrementing this
// local value of x will not change the value of x declared in the
// outer scope. That's because this `parallel block` has its own copy of x.
x++;      
}

// This value of x will change.
int x = 5;
#pragma omp parallel
{
// The x variable is now shared between each thread. What you
// do to x here changes the value of x in the outer scope.
#pragma omp critical
{
x++;         
}             
}

我最近经常看到人们使用捕获作为"处理"某些数据的一种方式。尽管您可以执行此操作,但捕获更通常用于访问对象的特定实例(有点像绑定(。

我认为这是"更好"(也许更通用(,如果您可以将数据作为参数传递,或者如果您想共享数据,则可以通过引用传递:

所以代替:

auto f =[&]{return s;};

你可以做:

auto f =[](double dbl){return dbl;}; // by copy
auto f =[](double &dbl){return dbl;}; // or by ref

然后像这样使用:

s = 5+i;
double a1 = f(s); // Here pass "s" into the lambda

所以,我并不是说不要使用捕获,有时这很好,但看起来你最好在这里使用参数。