C ++ 11只能将原始数据类型声明为原子数据吗?

时间:2013-05-23 13:21:18

标签: object c++11 thread-safety mutex atomic

我想知道,在C ++ 11中只能将原始数据类型声明为std :: atomic吗?例如,是否有可能声明库类对象被“原子地”变异或访问?

例如,我可能有

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;
}

但是,如果在不同的线程中调用这些setter和getter方法,我认为这可能会导致未定义的行为。如果我可以声明foo之类的话会很好:

std::atomic<time_point<high_resolution_clock>> foo;

...所以foo上的所有操作都将以原子方式进行。在我的项目的应用程序中,可能有数百个这样的foo变量在几十个类中声明,我觉得使对象变异并访问“原子”可以说更方便,而不必声明和lock_guard遍布整个地方的互斥。

这是不可能的,还是有更好的方法,还是我真的必须在所有地方使用互斥锁和lock_guard?

更新

  • 任何接受者?我一直在网上钓鱼以获得不错的信息,但是使用原子的例子很少,我无法确定它的应用程度。

2 个答案:

答案 0 :(得分:3)

atomic<>不限于基本类型。允许atomic<>使用T atomic<>类型std::mutex。来自c ++ 11标准的 29.5原子类型部分(它也在trivially copyable处说明):

  

有一个泛型类模板atomic。模板参数T的类型应该是可以轻易复制的(3.9)。

如果需要原子访问的对象不能与lock_guard<>一起使用,则定义包含原始对象和template的新对象。这意味着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(); 仅在新线程安全对象的getter和setter中使用,而不是在整个代码中乱丢。 {{1}}可能能够定义所需的线程安全机制:

{{1}}

答案 1 :(得分:0)

Atomics仅限于简单的可复制类(即没有自定义复制构造函数的类,其成员也可以轻易复制)。

这个要求对原子有很大的好处:

  • 没有原子操作可以抛出,因为构造函数抛出
  • 可以使用锁(自旋锁或互斥锁)和memcpy对所有原子进行建模以复制数据。
  • 所有原子都有一个有限的运行时间(有界)。

后者特别有用,因为有时使用自旋锁来实现原子,并且非常希望在持有自旋锁的同时避免无限制的任务。如果允许任何构造函数,实现往往需要回退到完全成熟的互斥锁,这对于非常小的关键部分来说比自旋锁慢。