简体   繁体   中英

Is is worth to declare a size_t as std::atomic if used across 2 threads?

I have a size_t variable which is updated by a std::thread and read by another std::thread .

I know that I can mutex protect the reads and writes. But, would it be the same or would it be beneficial if I make the size_t as std::atomic<size_t> ?

Yes, it is worth it. In fact it is mandatory to use std::atomic or synchronize access to a non-atomic if multiple threads use the same variable and at least one is writing to the variable. Not following this rule is data-race undefined behavior.

Depending on your use of the std::size_t the compiler can assume that non-atomic and otherwise non-synchronized variables will not change from other threads and optimize the code accordingly. This can cause Bad Things™ to happen.

My usual example for this is a loop where a non-atomic boolean is used:

// make keepRunning an std::atomic<bool> to avoid endless loop
bool keepRunning {true};
unsigned number = 0;

void stop()
{
    keepRunning = false;
}

void loop()
{
    while(keepRunning) {
        number += 1;
    }
}

When compiling this code with optimizations enabled, GCC and Clang will both only check keepRunning once and then start an endless loop. See https://godbolt.org/z/GYMiLE for the generated assembler output.

ie they optimize it into if (keepRunning) infinite_loop; , hoisting the load out of the loop. Because it's non-atomic, they're allowed to assume no other thread can be writing it. See Multithreading program stuck in optimized mode but runs normally in -O0 for a more detailed look at the same problem.

Note that this example only shows the error if the loop body is sufficiently simple. However the undefined behaviour is still present and should be avoided by using std::atomic or synchronization.


In this case you can use std::atomic<bool> with std::memory_order_relaxed because you don't need any synchronization or ordering wrt. other operations in either the writing or reading thread. That will give you atomicity (no tearing) and the assumption that the value can change asynchronously, without making the compiler use any asm barrier instructions to create more ordering wrt. other operations.

So it's possible and safe to use atomics without any synchronization, and without even creating synchronization between the writer and reader the way seq_cst or acquire/release loads and stores do. You can use this synchronization to safely share a non-atomic variable or array, eg with atomic<int*> buffer that the reader reads when the pointer is non-NULL.

But if only the atomic variable itself is shared, you can just have readers read the current value, not caring about synchronization. You may want to read it into a local temporary if you don't need to re-read every iteration of a short loop, only once per function call.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM