7

我在 for 循环中有一个 lambda,在 lambda 中有循环变量参数。当我运行它时,我希望输出数字 0-9。但由于它是一个 lambda,x 不会立即得到评估。

    for( int x = 0; x < n; ++x)
    {
            vec.push_back(thread{[&x](){
                    m.lock();
                    cout << x << endl;
                    m.unlock();
            }});
    }

输出:

0
3
3
9

等等

其他语言的解决方案是创建一个临时变量,

    for( int x = 0; x < n; ++x)
    {
            int tmp = x;
            vec.push_back(thread{[&tmp](){
                    m.lock();
                    cout << tmp << endl;
                    m.unlock();
            }});
    }

但这似乎不起作用。

请参阅接收错误参数的线程

奖金:

在我寻找答案的过程中,我偶然发现了这个问题 Generalizing C++11 Threads class to work with lambda ,它建议不要使用会使迭代器失效的容器。为什么会这样/

4

3 回答 3

13

指定捕获时,可以在按值捕获和按引用捕获之间进行选择。您已选择通过引用进行捕获。通过引用捕获意味着 lambda 函数内部的变量引用的是同一个对象。这意味着对该变量的任何更改都将被共享,并且您还需要确保引用的对象在 lambda 函数的生命周期内一直存在。

您可能打算按值捕获。为此,您可以将捕获规范替换为 become[=]或 to become [x]。后者确保只能x访问,而前者将允许访问其他变量。

顺便说一句,我建议不要显式使用lock()andunlock()而是使用其中一个锁卫士。有了这个,你的循环体看起来像这样:

vec.push_back(std::thread{[x](){
    std::lock_guard<std::mutex> kerberos(m);
    std::cout << x << "\n";
}});
于 2012-09-17T00:17:49.547 回答
6

如果要复制,请按而不是按引用捕获参数:

vec.push_back(std::thread{[x](){
  m.lock();
  std::cout << x << std::endl;
  m.unlock();
}});

这将复制x创建 lambda 对象时的值(而不是在线程启动时)。

在我寻找答案的过程中,我偶然发现了这个问题 Generalizing C++11 Threads class to work with lambda,它建议不要使用会使迭代器失效的容器。为什么会这样/

因为它谈论的是直接使用 pthreads 库的完全不同的实现。您正在使用std::thread,它旨在在 C++ 中工作。

于 2012-09-17T00:09:13.223 回答
3

问题是您正在通过引用捕获 x 。因此,当 x 在每个循环结束时递增时,这会反映在线程中。

您希望按值捕获 x,以便 lambda 仅在创建 lambda 时使用 x 的值。

for( int x = 0; x < n; ++x)
{               
   vec.push_back(thread{[x](){
      m.lock();
      cout << tmp << endl;
      m.unlock();
   }});
}
于 2012-09-17T00:10:00.690 回答