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

10,184

Solution 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.

Solution 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.

Solution 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.

Share:
10,184
Matt Kline
Author by

Matt Kline

Embedded software engineer, typography &amp; design enthusiast. BS in Computer Engineering and Computer Sciences from the University of Wisconsin-Madison Interests: Rust, hardware-software interface, performance-critical design, technical writing &amp; presentation

Updated on June 04, 2022

Comments

  • Matt Kline
    Matt Kline almost 2 years

    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?

    • Matt Kline
      Matt Kline over 11 years
      @NicolBolas How so? Isn't locking a variable with a mutex before accessing it a standard way to share data between threads?
  • Matt Kline
    Matt Kline over 11 years
    Why does it attempt a copy? Doesn't giving the variable name for a reference argument traditionally pass that variable by reference?
  • Stephan Dollberg
    Stephan Dollberg over 11 years
    @slavik262 yes, that is true. The thread constructor does that, as you are invoking a function from which you don't know when it will execute, if you passed a reference, the reference might be invalid, thus it will copy your arguments to safely store them. If you explicitly use a ref-wrapper, it is your job to make sure that the object will outlive the thread.
  • Matt Kline
    Matt Kline over 11 years
    Thanks! I'm well aware of lambdas (they're awesome) - I just wanted to minimize the amount of C++11 magic I was using for my test case to isolate the issue.
  • David Schwartz
    David Schwartz over 8 years
    Most of the time, you want to pass by value to a new thread. So that's the default.