Lambda inside loop

2019-06-17 12:15发布

I have a lambda inside a for loop with the loop variable parameter in the lambda. When I run it, I expect the numbers 0-9 to be output. But since it is a lambda, the x doesn't get evaluated immediately.

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

Output:

0
3
3
9

etc.

The solution for other languages would be to create a temporary variable,

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

but that doesn't seem to work.

see Threads receiving wrong parameters

Bonus:

In my search for an answer, I stumbled upon this question Generalizing C++11 Threads class to work with lambda which recommends not using a container that would invalidate the iterators. Why would that be/

标签: c++ lambda c++11
3条回答
做个烂人
2楼-- · 2019-06-17 12:35

The issue is that you are capturing the x by reference. Thus when x is incremented at the end of each loop, that is reflected in the thread.

You want to capture x by value so that the lambda only uses the value of x when the lambda is created.

for( int x = 0; x < n; ++x)
{               
   vec.push_back(thread{[x](){
      m.lock();
      cout << tmp << endl;
      m.unlock();
   }});
}
查看更多
劫难
3楼-- · 2019-06-17 12:44

When you specify the capture, you can choose between capture by value and capture by reference. You have chosen to capture by reference. Capturing by reference means that the variable inside the lambda function is referring to the same object. The implication is that any changes to this variable will be shared and you also need to make sure that the referenced object stays around for the life-time of the lambda function.

You probably meant to capture by values. To do this, you can either replace the capture specification to become [=] or to become [x]. The latter makes sure that only x can be accessed while the former would allow other variables to be accessible.

BTW, I'd recommend not using lock() and unlock() explicitly but rather use one of the lock guards. With this, the body of your loop would look something like this:

vec.push_back(std::thread{[x](){
    std::lock_guard<std::mutex> kerberos(m);
    std::cout << x << "\n";
}});
查看更多
4楼-- · 2019-06-17 12:55

Capture the parameter by value instead of by reference if you want to make a copy:

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

This will copy the value of x at the time the lambda object was created (not at the time the thread was started).

In my search for an answer, I stumbled upon this question Generalizing C++11 Threads class to work with lambda which recommends not using a container that would invalidate the iterators. Why would that be/

Because it's talking about a completely different implementation which directly uses the pthreads library. You're using std::thread, which is designed to work in C++.

查看更多
登录 后发表回答