Using atomics with std::thread in c++11

2019-04-27 07:51发布

问题:

I have a thread that I want to sit in a loop until I'm ready to exit the program, at which point I want it to break out of the loop and exit so I can call std::thread::join on it. In the days of c++03, I would just use a bool protected by a lock in order to tell the thread when to exit. This time I thought I would take advantage of the new atomics library (specifically std::atomic_bool), but I'm having trouble. Below is my test case:

#include <atomic>
#include <thread>
#include <cstdio>

using namespace std;

void setBool(atomic_bool& ab)
{
    ab = true;
}

int main()
{
    atomic_bool b;
    b = false;
    thread t(setBool, b);
    t.join();
    printf("Atomic bool value: %d\n", b.load());
    return 0;
}

The declaration of thread t spits out this monstrosity when I try to compile. The central part of the error seems to be:

invalid initialization of non-const reference of type ‘std::atomic_bool&’ from an rvalue of type ‘std::atomic_bool’

Why can I not get a reference to an atomic_bool? What should I do instead?

回答1:

You have to explicitly pass a ref to your thread. Using std::ref will create a std::reference_wrapper which is copyable and will carry the ref to your function.

thread t(setBool, std::ref(b));

Otherwise it will try to copy your atomic, which is uncopyable.



回答2:

As bamboon explained, you have to wrap objects with std::ref if you really want them to be passed by reference via std::thread's variable-argument constructor. (The same applies to std::async.) To avoid this counterintuitive behavior, you can use a lambda, which behaves precisely as you would expect. Just use this to create the thread:

thread t([&]{ setBool(b); });

With lambdas, there is no need for the ref/cref nonsense when you want to pass arguments by reference.



回答3:

I just ran into this as well, I think you could also solve your problem by passing an address-to the atomic like so:

std::atomic<bool> b{false};
std::thread t(setBool, &b);

Where your function takes a pointer to the atomic:

void setBool(std::atomic<bool>* ab)

I'm not sure what the accepted practice is, I suppose you could pass a null pointer to the atomic this way but I'm not sure why you would want to.