Question about atomic operations ...
Take, say, increment, the following is done:

1)грузим в регистр значение переменной 2)инкрементируем 3)выгружаем из регистра в память 

If we start doing this from different streams, then we can read the old value, this is understandable ...

However, with the help of some kind of magic, atomic types from different streams allow this to be turned without errors? What does an atomic type mean? Is this property of a certain type or can it be guaranteed for any type?
Well, the code:

 #include <iostream> #include <atomic> #include <thread> #include <chrono> int main() { using namespace std::chrono_literals; //std::atomic<long long> i{1}; //std::atomic<long long> ii{2}; long long i = 1; long long ii = 2; std::thread t1([&](){ i++; i--; ii++; }); std::thread t2([&](){ i++; i--; ii++; }); std::thread t3([&](){ i++; i--; ii++; }); std::thread t4([&](){ i++; i--; ii++; }); t1.join(); t2.join(); t3.join(); t4.join(); std::this_thread::sleep_for(100ms); std::cout<<i<<" "<<ii; } 

why what for atomic types, what for non-atomic types always shows 1 6 ... there should not be such

  • one
    You do a little bit longer - you have the same instantaneous execution without any switching! - Harry
  • @Harry, do a bit longer exactly what? - xperious
  • one
    See my answer. Streams to make more authentic. For some nanoseconds flow switching will not be! - Harry
  • one
    However, with the help of some magic, atomic types from different streams allow this to be turned without errors? - synchronized memory access - etki

1 answer 1

You have not given a single chance :) - such a short stream will have time to do everything and complete without switching.

For example,

  long long i = 1; long long ii = 2; std::thread t1([&](){ for(int q = 0; q < 1000000; ++q){ i++; i--; ii++;}}); std::thread t2([&](){ for(int q = 0; q < 1000000; ++q){ i++; i--; ii++;}}); std::thread t3([&](){ for(int q = 0; q < 1000000; ++q){ i++; i--; ii++;}}); std::thread t4([&](){ for(int q = 0; q < 1000000; ++q){ i++; i--; ii++;}}); t1.join(); t2.join(); t3.join(); t4.join(); //std::this_thread::sleep_for(100ms); - это нафиг не нужно!!! std::cout<<i<<" "<<ii << std::endl; } 

gives when compiling VC ++ 2015 and running on a quad-core processor something like

 4794 1815494 

Change to atomic - and get (with a noticeable delay)

 1 4000002 

All clear?

  • hmm, yes, thanks, just about the theory would be more ... - xperious
  • The implementation of atomic types, as I recall, is not spelled out in the standard. To ensure atomicity, a variety of options can be used - critical sections, mutexes, specific processor commands - i.e. anything that prevents the interruption of the operation being performed and switching to another thread. The concrete implementation depends on the compiler, operating system, hardware ... - Harry
  • You can also do atomic access yourself - the easiest way is with mutexes. At each operation with a variable, capture a mutex, after - release. - Harry
  • Well, as I understand it, atomic operations were created just so as not to block using mutexes because very heavy ... probably implementing atomic operations through a mutex is too stupid - xperious
  • one