C ++ 11 Can only primitive data types be declared atomic?

I was wondering if only primitive std :: atomic data types can be declared in C ++ 11? Is it possible, say, to declare an object of a library class "atomically" mutated or accessible?

For example, I may 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 on different threads, I think this can lead to 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 will be atomic. There are probably hundreds of such foo variables declared in dozens of classes in the application for my project, and I believe that it would be much more convenient to make the object mutable and accessible "atomic", so to speak, instead of declaring and blocking_guard mutexes everywhere.

Is this not possible, or is there a better approach, or do I really need to use the mutex and lock_guard everywhere?

Update

  • Any takers? I fished on the Internet to get decent information, but there are not so many examples with atomic quantities that I can’t be sure to what extent this can be applied.
+7
source share
2 answers

atomic<> not limited to primitive types. It is allowed to use atomic<> with type T , which is trivially copied . From section 29.5 Atomic types of the C ++ 11 standard (it is also specified in std::atomic ):

There is a common template template. The argument type of the template T must be trivially copied (3.9).

If objects that require atomic access cannot be used with atomic<> , then new objects containing the original object are defined, and std::mutex . This means that lock_guard<> used internally by the receiver and setter only for a new object protected by the stream and is not clogged in the entire code. A template could determine the required flow safety equipment:

 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(); 
+3
source

Atoms are limited to trivially stitched classes (that is, classes that do not have their own copy constructor, and whose members can also be trivially copied).

This requirement has huge advantages for atomic science:

  • No atomic operation can be selected because the constructor threw
  • All atoms can be modeled using a lock (spin lock or mutex) and memcpy to copy data.
  • All atoms have finite runtime (limited).

The latter is especially useful, since atoms are sometimes realized with the help of spindle hairpins, and it is highly advisable to avoid unlimited tasks by holding a spin lock. If any constructor were allowed, implementations would usually need to step back from full-sized mutexes, which are slower than screw blocks for very small critical sections.

0
source

All Articles