2014-10-04 117 views
1

下面我们有容器的数据。我们希望多个线程能够搜索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; 
}; 

容器在地图中包含共享点。不同的线程将查找容器中的对象并查找对象中的字段。

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; 
}; 
+1

对于初学者来说,只有'p0'和'p1'是同一个数组对象的一部分才会定义'p0 ()(p0,p1)',尽管这个操作是定义的,即使'p0'和'p1'不是同一个数组的一部分。 – 2014-10-04 19:21:31

+0

使用该地址确保锁定顺序具有创造性,但您可能不想尝试实现您自己的STL类型的并发版本。这可能是[codereview](http://codereview.stackexchange.com)更好的帖子。 – Jason 2014-10-04 19:37:52

+0

好了解。我必须为每个CData使用一个唯一的ID,这不应该太难。 – steviekm3 2014-10-04 20:00:28

回答

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:也CDataContainer同步::实例是不必要的在C++11和它现在是完全安全的做:

CDataContainer& instance() 
{ 
    static CDataContainer s_instance; 
    return s_instance; 
} 
+0

如果你看他的课,他没有插入它。如果事实证明他确实需要该功能,那么仍然可以在保持不变性和线程安全性的同时进行更新,如我参考的链接所示。 – sjdowling 2014-10-04 19:38:35

+0

啊!对不起,我看了一下错误的课。 – dyp 2014-10-04 19:53:02

+0

但是你有一个'insert'成员函数..你打算拷贝'CData'的'find'成员函数吗? – dyp 2014-10-04 19:54:41

0

此问题可能属于codereview (至少这是我听说过的)。

对于CData::operator=()你可能避免对锁定顺序或地址的比较问题,通过执行类似:

CData& operator=(CData tmp) 
{ 
    lock_guard<mutex> guard(m_lock); 
    m_data=std::move(tmp.m_data); 
} 

我认为这是更简单,更清晰,我怀疑有任何显著的性能影响。除了古怪的自我分配的情况下,它仍然会正常工作,但会采取几个锁,并不必要地复制m_data

您可能还想定义移动复制和分配成员。