【问题标题】:Is an atomic object unsafe in any context where a plain object is safe?在普通对象安全的任何上下文中,原子对象是否不安全?
【发布时间】:2019-10-18 02:38:16
【问题描述】:

我的问题很简单:是否有任何并行算法,其中使用 T 的普通类型的对象是合法且安全的(定义明确,保证工作),而使用 std::atomic<T> 对象会导致未指定或未定义的行为? 换句话说,非原子类型有可能提供比原子类型更强的保证吗?

当原子对象对常规对象有效时,内存可见性的所有保证是否都有效?

[这意味着编译器可以同等对待(宽松的)原子和非原子操作,从而简化中间表示。]

如果是,这是否可以正式证明?

当然类型T 使得std::atomic<T> 不是std::atomic 的有效特化不计算在内。

【问题讨论】:

  • 不,没有这样的上下文。
  • vector<bool> 是特殊的(压缩位数组),vector<atomic<bool>> 不是。但是vector<atomic<T>> 几乎无法使用(不可复制,因此只有固定大小的构造和[] 访问工作,而不是push_back),我不认为这是你的想法。 (你可以想象通过硬编码一些关于对象表示或大小的假设来打破它,也许,如果.data() 甚至可以被vector<bool> 访问)。
  • @PeterCordes 完全不是我想知道的,但仍然值得一提。

标签: c++ language-lawyer race-condition memory-model stdatomic


【解决方案1】:

atomic 等效值相比,非原子值具有更强的保证。

我能想到的唯一不好的模式是,当开发人员错误地假设如果数据结构的所有成员都是单独线程安全的,那么整个数据结构就被认为是线程安全的。这不是一个正确的假设。

考虑以下假设:

class Volume
{
    atomic<int> _width;
    atomic<int> _height;
    atomic<int> _depth;
public:
   int computeValue() {return (_width*_height*_depth);}
   void SetInternals(int w, int h, int d) {_height=h;_width=w;_depth=d;}
};

线程 1 这样做的地方:

f->SetInternals(42, 100, 200);

线程 2 同时执行此操作。

int something_important = f->computeValue();

如果两个线程同时执行,可以看到computeValue返回的值可能会损坏。

您可以使用互斥锁和锁组合代替原子实现所需的线程安全行为:

class Volume
{
    int _width;
    int _height;
    int _depth;
    mutex _mutex;
public:
   int computeValue() {
        lock_guard<mutex> lck(_mutex);
        int result = _width*_height*_depth;
        return result;
   }
   void SetInternals(int w, int h, int d) {
      lock_guard<mutex> lck(_mutex);
      _height=h;_width=w;_depth=d;
   }
};

【讨论】:

  • 当然,如果您使用互斥体原子,它仍然是安全的。因此,这与 OP 所问的不太一样,如果您刚刚取出原子,它会突然变得更安全。
猜你喜欢
  • 2011-01-28
  • 2017-02-17
  • 2010-12-26
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 2011-09-22
  • 2013-01-23
  • 1970-01-01
相关资源
最近更新 更多