Nick
Nick

Reputation: 10539

Can I use no thread synchronization here?

I am researching mutexes.

I come up with this example that seems to work without any synchronization.

#include <cstdint>
#include <thread>

#include <iostream>

constexpr size_t COUNT = 10000000;

int g_x = 0;

void p1(){
    for(size_t i = 0; i < COUNT; ++i){
        ++g_x;
    }
}

void p2(){
    int a = 0;

    for(size_t i = 0; i < COUNT; ++i){
        if (a > g_x){
            std::cout << "Problem detected" << '\n';
        }

        a = g_x;
    }
}

int main(){
    std::thread t1{ p1 };
    std::thread t2{ p2 };

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

    std::cout << g_x << '\n';
}

My assumptions are following:

Thread 1 change the value of g_x, but it is the only thread that change the value, so theoretically this suppose to be OK.

Thread 2 reads the value of g_x. Reads suppose to be atomic on x86 and ARM. So there must be no problem there too. I have example with several read threads and it works OK too.

With other words, write is not shared and reads are atomic.

Are the assumptions correct?

Upvotes: 2

Views: 52

Answers (1)

Eran
Eran

Reputation: 22020

There's certainly a data race here: g_x is not an std::atomic; it is written to by one thread, and read from by another. So the results are undefined.

Note that the CPU memory model is only part of the deal. The compiler might do all sorts of optimizations (using registers, reordering etc.) if you don't declare your shared variables properly.

As for mutexes, you do not need one here. Declaring g_x as atomic should remove the UB and guarantee proper communication between the threads. Btw, the for in p2 can probably be optimized out even if you're using atomics, but I assume this is just a reduced code and not the real thing.

Upvotes: 4

Related Questions