Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ atomic read/write misunderstanding

Why program with this code sometimes prints "2" ?

int main() {
    std::atomic<int> a;
    a = 0;

    std::thread t1([&]{++a;});
    std::thread t2([&]{a++;});
    std::thread t3([&]{
        a = a.load() + 1;
    });

    t1.join();
    t2.join();
    t3.join();

    if (a != 3) {
        std::cout << "a: " << a << std::endl;
    }
}

I've thought std::atomic guarantees that all operations will be done atomically so writing here(incrementing) will use a memory barrier and we will have always 3 at the end of threads work. I've explored the code and found out that the problem thread is t3 but I can't understand why it is wrong.

like image 653
Victor Polevoy Avatar asked Nov 27 '14 22:11

Victor Polevoy


People also ask

What is Atomics in C programming?

Atomics as part of the C language are an optional feature that is available since C11. Their purpose is to ensure race-free access to variables that are shared between different threads. Without atomic qualification, the state of a shared variable would be undefined if two threads access it concurrently.

What is the C++11 atomic library?

In C++11, there is finally a way to perform truly portable atomic loads and stores: the C++11 atomic library. Atomic loads and stores performed using the C++11 atomic library would even work on the imaginary computer above – even if it means the C++11 atomic library must secretly lock a mutex to make each operation atomic.

Is it possible to do an atomic operation on a type?

Only operations on data can ever be atomic. Additionally, the std::atomic<> types are designed in such a way where only atomic operations are meant to be applied to the data a type represents, and never to intermix atomic and non-atomic operations. The most basic atomic operations are loads and stores.

When should two threads use atomic operations on shared variables?

We can formulate it as a rule: Any time two threads operate on a shared variable concurrently, and one of those operations performs a write, both threads must use atomic operations.


Video Answer


1 Answers

t3, unlike the two other threads, does not perform an atomic add. Instead, it atomically loads a, performs the arithmetic (add 1) on a temporary, and atomically stores that new value back to a. This overwrites a regardless of atomic operations that might have happened in between.

So you can have the following scenario:

  1. t1 or t2 atomically increment a which is now equal to 1.
  2. t3 atomically loads 1.
  3. t1 or t2 atomically increment a which is now equal to 2.
  4. t3 performs a non-atomic add on the previously loaded value and atomically stores back 2.
like image 157
user703016 Avatar answered Oct 06 '22 06:10

user703016