下面我们有数据容器。我们希望多个线程能够搜索Container并获取Data对象。
#include <boost/thread.hpp>
using boost::shared_ptr;
using boost::mutex;
using boost::lock_guard;
using std::string;
class CData
{
public:
bool find(string& value, const string& fieldName)
{
lock_guard<mutex> guard(m_lock);
auto it=m_data.find(fieldName);
if( it!=m_data.end())
{
value=it->second;
return true;
}
return false;
}
CData(const CData& rhs)
{
lock_guard<mutex> guard(rhs.m_lock);
m_data=rhs.m_data;
}
CData& operator=( const CData& rhs )
{
if ( this == &rhs )
{
return *this;
}
mutex* lock1;
mutex* lock2;
if(this<&rhs)
{
lock1=&m_lock;
lock2=&rhs.m_lock;
}
else
{
lock1=&rhs.m_lock;
lock2=&m_lock;
}
lock_guard<mutex> guard1(*lock1);
lock_guard<mutex> guard2(*lock2);
m_data=rhs.m_data;
}
private:
std::map<string,string> m_data;
mutable mutex m_lock;
};
Container在地图中保存共享的pts。不同的线程将查找容器中的对象并查找对象中的字段。
class CDataContainer
{
public:
CDataContainer* instance()
{
static CDataContainer* s_instance;
static mutex s_instanceLock;
lock_guard<mutex> guard(s_instanceLock);
if(!s_instance)
{
s_instance=new CDataContainer;
}
return s_instance;
}
void insert(const string& key, const shared_ptr<CData>& data)
{
lock_guard<mutex> guard(m_lock);
m_key2data[key]=data;
}
void erase(const string& key)
{
lock_guard<mutex> guard(m_lock);
m_key2data.erase(key);
}
bool find(shared_ptr<CData>& data,const string& key)
{
lock_guard<mutex> guard(m_lock);
auto it=m_key2data.find(key);
if( it!=m_key2data.end())
{
data=it->second;
return true;
}
return false;
}
private:
CDataContainer()
}
mutex m_lock;
std::map<string,shared_ptr<CData>> m_key2data;
};
答案 0 :(得分:2)
也许,但是对于所有这些互斥体来说,它可能效率不高。如果我可以提出一个小修改:
class CData
{
public:
void insert(const string& key, const shared_ptr<CData>& data) const;
...
private:
const std::map<string,string> m_data;
};
现在,您不需要CData
中的任何互斥锁,您可以更加确信其线程安全性。真正的多线程前进方向是从常量的角度来考虑它。我建议阅读Bartosz's blog,其中他展示了如何在没有互斥体的情况下编写线程安全的C ++,或者仅使用不变性来编写同步。
编辑作为补充说明,获取多个互斥锁而没有死锁的正确方法是使用std::lock
,如:
std::unique_lock<std::mutex> lk1(m_lock, std::defer_lock);
std::unique_lock<std::mutex> lk2(rhs.m_lock, std::defer_lock);
std::lock(lk1, lk2);
Edit2:在C++11中也不需要CDataContainer :: instance中的同步,现在可以完全安全了:
CDataContainer& instance()
{
static CDataContainer s_instance;
return s_instance;
}
答案 1 :(得分:0)
这个问题可能属于codereview(至少是我所听到过的)。
对于CData::operator=()
,您可以通过执行以下操作来避免有关锁定顺序或比较地址的问题:
CData& operator=( CData tmp )
{
lock_guard<mutex> guard(m_lock);
m_data=std::move(tmp.m_data);
}
我认为它更简单,更清晰,我怀疑是否有任何重大的性能影响。除了奇怪的自我分配案例,它仍然可以正常工作,但会不必要地锁定并复制m_data
。
您可能还想定义移动副本和分配成员。