简体   繁体   中英

Difference between atomic<int> and int

I want to know if there is any different between std::atomic<int> and int if we are just doing load and store. I am not concerned about the memory ordering. For example consider the below code

int x{1};

void f(int myid) {

    while(1){
        while(x!= myid){}
        //cout<<"thread : "<< myid<<"\n";
        //this_thread::sleep_for(std::chrono::duration(3s));
        x = (x % 3) + 1;
    }
}

int main(){

    thread x[3];
    for(int i=0;i<3;i++){

        x[i] = thread(f,i+1);
    }

    for(int i=0;i<3;i++){

        x[i].join();
    }
}

Now the output (if you uncomment the cout) will be

Thread :1

Thread :2

Thread :3

...

I want to know if there is any benefit in changing the int x to atomic<int> x ?

Consider your code:

void f(int myid) {
    while(1){
        while(x!= myid){}
        //cout<<"thread : "<< myid<<"\n";
        //this_thread::sleep_for(std::chrono::duration(3s));
        x = (x % 3) + 1;
    }
}

If the program didn't have undefined behaviour, then you could expect that when f was called, x would be read from the stack at least once, but having done that, the compiler has no reason to think that any changes to x will happen outside the function, or that any changes to x made within the function need to be visible outside the function until after the function returns, so it's entitled to read x into a CPU register, keep looking at the same register value and comparing it to myid - which means it'll either pass through instantly or be stuck forever.

Then, compilers are allowed to assume they'll make progress (see Forward Progress in the C++ Standard), so they could conclude that because they'd never progress if x != myid , x can't possibly be equal to myid , and remove the inner while loop. Similarly, an outer loop simplified to while (1) x = (x % 3) + 1; where x might be a register - doesn't make progress and could also be eliminated. Or, the compiler could leave the loop but remove the seemingly pointless operations on x .

Putting your code into the online Godbolt compiler explorer and compiling with GCC trunk at -O3 optimisation, f(int) code is:

f(int):
.L2:
    jmp     .L2

If you make x atomic, then the compiler can't simply use a register while accessing/modifying it, and assume that there will be a good time to update it before the function returns. It will actually have to modify the variable in memory and propagate that change so other threads can read the updated value.

I want to know if there is any benefit in changing the int x to atomic x?

You could say that. Turning int into atomic<int> in your example will turn your program from incorrect to correct (*).

Accessing the same int from multiple threads at the same time (without any form of access synchronization) is Undefined Behavior.


*) Well, the program might still be incorrect, but at least it avoids this particular problem.

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