这种类型的编码线程是否安全,死锁是否安全?

时间:2014-10-04 18:55:19

标签: c++ boost thread-safety

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

2 个答案:

答案 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

您可能还想定义移动副本和分配成员。