具有不可复制的boost :: mutex的类的赋值运算符

时间:2011-01-25 09:33:47

标签: c++ multithreading boost-thread

我正在这里阅读这个旧的Boost Thread FAQ,其中有一个指南,用于为具有boost::mutex不可复制对象作为成员的类实现复制构造和赋值运算符。

我对复制构造函数很好,但我对赋值运算符有一些疑问。 以下说明仍然有效吗?

  // old boost thread    
  const counter & operator=( const counter& other ){
     if (this == &other)
        return *this;

     boost::mutex::scoped_lock lock1(&m_mutex < &other.m_mutex ?
                                        m_mutex : other.m_mutex);
     boost::mutex::scoped_lock lock2(&m_mutex > &other.m_mutex ?
                                        m_mutex : other.m_mutex);
     m_value = other.m_value;

     return *this;
}

不应该更新为:

 // new boost thread    
 const counter& operator=(const counter& other){
    if (this == &other)
       return *this;

    boost::unique_lock<boost::mutex> l1(m_mutex, boost::defer_lock);
    boost::unique_lock<boost::mutex> l2(other.m_mutex, boost::defer_lock);
    boost::lock(l1,l2);
    m_value = other.m_value;

    return *this;
}

1 个答案:

答案 0 :(得分:5)

首先,我假设问题是在锁定多个任意互斥锁时避免死锁。重要的是始终使用一组互斥锁在整个代码中使用相同的排序约定。如果你可以保证互斥锁A总是在B之前锁定,B总是在C之前锁定,而A总是在C之前锁定,你将避免死锁。

在第一个代码示例中,约定是首先使用较低的内存地址锁定互斥锁。这样可以正常工作,因为地址排序是不变的。第二个版本是避免死锁的official Boost method。文档未指定内部执行的排序。我不建议在源代码中查找它并在代码中的其他地方使用此信息 - 如果库发生更改,它可能会巧妙地破坏。

如果你从头开始(之前你的代码中没有一次持有多个互斥锁),那么使用Boost的方法绝对是可取的 - 你不必担心确切的排序你每次都把它放在一边。如果其余代码使用内存排序,则必须使用它。如果您的代码完全使用其他约定,那么您也需要在此处应用它。在任何情况下,你都不应该在任何可能同时举行的锁定中混合约定,这只是在寻找麻烦。

要回答评论中的问题:

自定义锁定顺序方案在某些情况下可能很有用,特别是如果你需要长时间持有一些锁(A),有些(B)只是暂时,持有长一。例如,如果您需要在类型A的对象上运行长作业,这会短暂地影响B的许多实例。约定总是首先获取A的锁定,然后 B上的锁定对象:

void doStuff(A& a, std::list<B*> bs)
{
  boost::unique_lock<boost::mutex> la(a.mutex); // lock a throughout
  for (std::list<B*>::iterator ib = bs.begin(); ib != bs.end(); ++ib)
  {
    // lock each B only for one loop iteration
    boost::unique_lock<boost::mutex> lb(ib->mutex);
    // work on a and *ib
    // ...
  }
}

可能能够在每次循环迭代之间放弃对A的锁定并使用Boost的/ C ++ 0x的锁定顺序,但是根据doStuff()的作用,这可能会使算法更多复杂或混乱。

另一个例子:在运行时环境中,对象不一定停留在同一个内存位置(例如由于复制垃圾收集),依赖内存地址进行排序将不可靠。因此,您可以为每个对象提供唯一的ID,并将锁定顺序基于ID顺序。