通过复制和交换与两个锁分配

时间:2011-02-21 22:36:58

标签: c++ c++11 mutex copy-and-swap

借用Howard Hinnant's example并修改它以使用copy-and-swap,这是op =线程安全吗?

struct A {
  A() = default;
  A(A const &x);  // Assume implements correct locking and copying.

  A& operator=(A x) {
    std::lock_guard<std::mutex> lock_data (_mut);
    using std::swap;
    swap(_data, x._data);
    return *this;
  }

private:
  mutable std::mutex _mut;
  std::vector<double> _data;
};

我相信这个线程安全(记得op ='s参数是按值传递的),我唯一能找到的问题就是在地毯下扫过的那个:复制ctor。但是,这将是一个罕见的类,它允许复制赋值,但不允许复制构造,因此在两种选择中都存在同样的问题。

鉴于自我分配是如此罕见(至少在这个例子中)我不介意额外的副本,如果它发生,考虑这个!=&amp; rhs的潜在优化可以忽略不计或悲观。与原始策略(下图)相比,是否还有其他理由更喜欢或避免它?

A& operator=(A const &rhs) {
  if (this != &rhs) {
    std::unique_lock<std::mutex> lhs_lock(    _mut, std::defer_lock);
    std::unique_lock<std::mutex> rhs_lock(rhs._mut, std::defer_lock);
    std::lock(lhs_lock, rhs_lock);
    _data = rhs._data;
  }
  return *this;
}

顺便说一句,我认为这简洁地处理了复制文件,至少在本课程中,即使它有点迟钝:

A(A const &x) : _data {(std::lock_guard<std::mutex>(x._mut), x._data)} {}

1 个答案:

答案 0 :(得分:8)

我相信你的作业是线程安全的(假设课程外没有参考)。它相对于const A&变体的性能可能取决于A.我认为对于许多A而言,如果不是更快,你的重写将同样快。我有一个很好的反例例子是std :: vector(以及类似的类)。

std :: vector的容量不参与其值。如果lhs相对于rhs具有足够的容量,那么重用那个容量而不是把它扔到临时状态,可以获得性能。

例如:

std::vector<int> v1(5);
std::vector<int> v2(4);
...
v1 = v2;

在上面的示例中,如果v1保持其执行赋值的能力,则可以在没有堆分配或释放的情况下完成分配。但是如果vector使用交换习惯用法,那么它会进行一次分配和一次释放。

我注意到,就线程安全而言,两种算法都锁定/解锁两个锁。虽然交换变体避免了同时锁定它们的需要。我相信平均锁定两者的成本很小。但在竞争激烈的用例中,它可能成为一个问题。