C++11 Can only primitive data types be declared atomic?
I was wondering, can only primitive data types be declared std::atomic in C++11? Is it possible, say, to declare a library class object to be "atomically" mutated or accessed?
For example, I might have
using namespace std::chrono;
time_point<high_resolution_clock> foo;
// setter method
void set_foo() {
foo = high_resolution_clock::now();
}
// getter method
time_point<high_resolution_clock> get_foo() {
return foo;
}
But, if these setter and getter methods are called in different threads, I think that may cause undefined behavior. It would be nice if I could declare foo something like:
std::atomic<time_point<high_resolution_clock>> foo;
...so that all operations on foo would be conducted in an atomic fashion. In the application for my project there are possibly hundreds of such foo variables declared across dozens of classes, and I feel it would be far more convenient to make the object mutating and accessing "atomic" so to speak, instead of having to declare and lock_guard mutexes all over the place.
Is this not possible, or is there a better approach, or do I really have to use a mutex and lock_guard everywhere?
Update :
atomic<>
is not restricted to primitive types. It is permitted to use atomic<>
with a type T
that is trivially copyable. From section 29.5 Atomic types of the c++11 standard (it also stated at std::atomic
):
There is a generic class template atomic. The type of the template argument T shall be trivially copyable (3.9).
If the objects for which atomic access is required cannot be used with atomic<>
then define new objects, containing the original object and a std::mutex
. This means the lock_guard<>
is used within the getter and setter only of the new thread safe object, and not littered throughout the code. A template
might be able to define the thread safety machinery required:
template <typename T>
class mutable_object
{
public:
mutable_object() : t_() {}
explicit mutable_object(T a_t) : t_(std::move(a_t)) {}
T get() const
{
std::lock_guard<std::mutex> lk(mtx_);
return t_;
}
void set(T const& a_t)
{
std::lock_guard<std::mutex> lk(mtx_);
t_ = a_t;
}
private:
T t_;
mutable std::mutex mtx_;
};
using mutable_high_resolution_clock =
mutable_object<std::chrono::time_point<
std::chrono::high_resolution_clock>>;
using mutable_string = mutable_object<std::string>;
mutable_high_resolution_clock c;
c.set(std::chrono::high_resolution_clock::now());
auto c1 = c.get();
mutable_string s;
s.set(std::string("hello"));
auto s1 = s.get();
Atomics are limited to trivially copyable classes (ie classes which have no custom copy constructor, and whose members are also trivially copyable).
This requirement has huge advantages for atomics:
The latter is particularly useful, as atomics are sometimes implemented using spinlocks, and it is highly desired to avoid unbounded tasks while holding a spinlock. If any constructor was allowed, implementations would tend to need to fall back on full blown mutexes, which are slower than spinlocks for very small critical sections.
链接地址: http://www.djcxy.com/p/13706.html