【问题标题】:Lambda inside loopLambda 内循环
【发布时间】:2012-09-09 05:25:58
【问题描述】:

我在 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();
            }});
    }

但这似乎不起作用。

Threads receiving wrong parameters

奖金:

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

【问题讨论】:

    标签: c++ lambda c++11


    【解决方案1】:

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

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

    顺便说一句,我建议不要明确地使用lock()unlock(),而是使用其中一种锁防护装置。有了这个,你的循环体看起来像这样:

    vec.push_back(std::thread{[x](){
        std::lock_guard<std::mutex> kerberos(m);
        std::cout << x << "\n";
    }});
    

    【讨论】:

    • 我觉得有点傻。我误解了 & 在 lambda 表达式中的用途。对于这个过于简单的例子,是的,lock_guard 是合适的。但在实际应用中,该功能做了很多不需要锁定的事情。在那种情况下,互斥锁只适合在重要部分周围进行锁定和解锁,对吗?
    • @SaulBack:不。无论哪种方式,您都应该使用lock_guard。如果您只想锁定某些代码,请使用带有{} 的显式块来包含锁定的代码。这是这里的基本 RAII 编码。
    • @SaulBack:尤其是在您想使用 RAII 的复杂代码中,例如,std::lock_guard&lt;std::mutex&gt;!在这个 trivial 示例中,我可以看到它不是必需的,但是当它有点复杂时,事情很快就会失控。如果您需要限制持有锁的范围,请使用范围,即一对大括号:{ &lt;lock and code needing it goes here&gt; }
    【解决方案2】:

    如果要复制,则通过而不是通过引用来捕获参数:

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

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

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

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

    【讨论】:

    • 你能详细说明为什么容器中的 ptheads 不能工作,而 std::thread 可以吗?是拷贝构造函数、移动构造函数、赋值运算符的问题吗?
    【解决方案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();
       }});
    }
    

    【讨论】:

      猜你喜欢
      • 2014-07-16
      • 2011-11-24
      • 2019-01-09
      • 2012-12-22
      • 2014-06-18
      • 2018-01-18
      相关资源
      最近更新 更多