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)
首先,我假设问题是在锁定多个任意互斥锁时避免死锁.重要的是始终使用一组互斥锁在整个代码中使用相同的排序约定.如果你可以保证互斥锁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顺序.