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

Abr*_*ile 4 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;
}
Run Code Online (Sandbox Code Playgroud)

不应该更新为:

 // 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;
}
Run Code Online (Sandbox Code Playgroud)

pmd*_*mdj 5

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

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

如果你是从头开始(之前你的代码中没有一次持有多个互斥锁),那么使用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顺序.