i'm confused about the atomic operation on c++11,
i know the atomic variable self increment is atomic operation,
but i use the assignment to other value, just doubt it.
the code just like:
//....
static std::atomic<int> i; // global variable
//....
// in the thread
int id = ++i;
when using the assignment at different threads, is the id unique value?
the test code:
#include <thread>
#include <mutex>
#include <atomic>
#include <iostream>
class A {
public:
static int idGenerator;
static std::mutex m;
A () {
// i know this operation will keep the id_ is unique
std::lock_guard<std::mutex> lock(m);
id_ = ++idGenerator;
}
void F(std::string name) {
std::cout << name << " " << id_ << std::endl;
}
private:
int id_;
};
int A::idGenerator = 0;
std::mutex A::m;
class B {
public:
static int idGenerator;
B () {
// after self increment, is the assignment atomic?
id_ = (++idGenerator);
}
void F(std::string name) {
std::cout << name << " " << id_.load() << std::endl;
}
private:
std::atomic<int> id_;
};
int B::idGenerator = 0;
void funcA() {
A a2;
a2.F("a2");
}
void funcB() {
B b2;
b2.F("b2");
}
int main() {
A a1;
B b1;
std::thread t1(&funcA);
std::thread t2(&funcB);
a1.F("a1");
b1.F("b1");
t1.join();
t2.join();
return 0;
}
there are three threads,
A class use lock_guard keep unique.
B class just use atomic operation, and assign to the variable
The increment-memory machine instruction on an X86 is atomic only if you use it with a LOCK prefix. x++ in C and C++ doesn't have atomic behavior.
An example of atomic operation is instruction execution, usually an instruction feed to the execution unit can't be stopped in the middle. Yet, a statement in high level language results in multiple instructions. It is the root cause of non-atomic operations.
On objects without an atomic type, standard never defines ++ as an atomic operation. C11 defines atomic types in stdatomic. h. If you have an object with an atomic type, a postfix and prefix operators ++ will define an atomic operation as: read-modify-write operation with memory_order_seq_cst memory order semantics.
Atomic operations are sequences of instructions that guarantee atomic accesses and updates of shared single word variables. This means that atomic operations cannot protect accesses to complex data structures in the way that locks can, but they provide a very efficient way of serializing access to a single word.
The specification of the atomic increment functions give a crucial insight into their behaviour - from http://en.cppreference.com/w/cpp/atomic/atomic/operator_arith for Integral T
types:
T operator++();
T operator++() volatile;
T operator++( int );
T operator++( int ) volatile;
Notice they return a T
by value, and never return the usual T&
from a pre-increment. For that reason, the "read" of post-incremented value is not a second distinct operation, and is part of the atomic increment operation itself.
See also the "Return Value" and "Note" text on the above-linked page.
static std::atomic<int> i; // global variable
// in the thread
int id = ++i;
when using the assignment at different threads, is the id unique value?
Yes.
C++ atomic variables ensure that ++i
will be evaluated atomically, so each values of id
on different threads are unique.
The expression id = ++i;
is executed following steps.
i
, and sub-expression(++i
) is evaluated post-increment value.id
. (this step is non-atomically)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With