Some thoughts on C++ atomic operation atomic

After learning about atomic operations, I have rarely used them, forgotten a lot, and still have many things I don’t understand. For example, a question that popped up recently:

std::atomicIn the process of assignment and comparison, are they atomic operations?

In order to verify the results, a demo is written here to verify a == 1whether a = 1the sum process is also an atomic operation.

#include <atomic>

using namespace std;
int main() {
  atomic<int> a;

  if(a == 1)
  {
      a = 2;
  }
  else if(a.load() == 3)
  {
      a.store(4);
  }
  a.load();
}

The assembly code is as follows:

main:
        mov     eax, DWORD PTR [rsp-4]
        cmp     eax, 1
        je      .L4
        mov     eax, DWORD PTR [rsp-4]
        cmp     eax, 3
        je      .L5
.L3:
        mov     eax, DWORD PTR [rsp-4]
        mov     eax, 0
        ret
.L4:
        mov     eax, 2
        xchg    eax, DWORD PTR [rsp-4]
        jmp     .L3
.L5:
        mov     eax, 4
        xchg    eax, DWORD PTR [rsp-4]
        jmp     .L3

It's not very intuitive to look at it this way. You can check it out on the godbolt website.

https://godbolt.org/z/n4bs3e3Kn

It is concluded from the compilation results:

  1. a == 1a.load() == 2is consistent with
  2. a = 2anda.store(2)

Then, I checked the relevant information and learned from the book "C++ Standard Library" that store()these load()operations are guaranteed to be atomic (uncuttable). At the same time, it was also mentioned:

For atomic types, you can continue to use useful and common operations, such as assignment, automatic conversion to integer, increment, decrement, etc.

std::atomic<bool> ab(false);
ab = true;
if(ab){
    ...
}

std::atomic<int> ai(0);
int x = ai;
ai = 10;
ai++;
ai-=17;

Well, here is further confirmation of the answer to the question.

Replenish

This passage needs to be savored carefully

store()A so-called release operation will be performed on the affected memory area to ensure that all previous memory operations, whether atomic or not, become "visible to other threads" before the store takes effect.

load()A so-called acquire operation will be performed on the affected memory area to ensure that all subsequent memory operations, whether atomic or not, become "visible to other threads" after load.

My understanding: "Affected memory area" refers to the memory area of ​​this atomic variable and the associated memory area. "Can be seen by other threads" is equivalent to hiding the process when performing atomic operations, and other threads can see it. If it is missing, then other threads cannot make changes. After performing the atomic operation, it will be displayed again, so that other threads can see it and perform read and write operations.
In short, store()the delete operation is performed on the memory area where it is located first, regardless of what the previous memory area looked like, because it has been cleared, and then the locked assignment operation is performed, and the lock is unlocked after the assignment is completed. load()First, lock the memory area where it is located, and then unlock it after obtaining the value.

Points to note

When initializing an atomic variable with val assignment, it is not an atomic operation.

std::atomic<T> a = val;//这个不是原子操作
std::atomic<T> b(val);//原子操作

Increment and decrement are actually overloaded operations, calling sum fetch_add()and fetch_sub()returning the copied value, +=sum -=is equivalent to fetch_add()andfetch_sub()

Guess you like

Origin blog.csdn.net/no_say_you_know/article/details/127393997