谈论shared_ptr的线程安全性

  • 一个shared_ptr对象可以同时被多个线程同时读取
  • 两个shared_ptr对象实体可以被两个线程同时写入,“析构”算写操作
  • 如果要从多个线程读写同一个shared_ptr对象,那么需要加锁
    从这个角度来看,shared_ptr 的线程安全级别和标准库容器差不多,所以在多线程中同时访问一个shared_ptr,象,正确做法是用mutex保护,并且尽量保证临界区范围小

shared_ptr是引用计数智能指针,如果当前只有一个观察者,那么引用计数的值就是1
对于write端,如果发现它的引用计数为1,那么可以安全的修改共享对象,不必担心有人读取它
对于read端,在读之前把引用计数加1,读完之后减1,这样保证在读的期间引用计数大于1,可以阻止读时写操作
那么,问题就在对于write端,如果发现引用计数大于1,该如何处理写,明确知道此时在读数据。sleep等待?
举一个简单的例子

typedef std::vector<Foo> FooList;
typedef std::shared_ptr<FooList> FooListPtr;
MutexLock mutex;
FooListPtr g_foos;

//读端
void traverse()
{
    FooListPtr foos;
    {
        MutexLockGuard lock(mutex);
        foos = g_foos;
        assert(!g_foos.unique());
    }
    for (auto iter = foos->begin(); iter != foos.end(); ++iter)
        iter->doit();
}

//写端
void post(const Foo& f)
{
    MutexLockGuard lock(mutex);
    if (!g_foos.unique())//如果正在写,说明有两个引用计数
    {
        g_foos.reset(new FooList(*g_foos));
    } 
    assert(g_foos.unique());
    g_foos->push_back(f);
}

上面例子,在写端,当有两个引用计数时,我们不能修改,而是复制一份,在副本上修改,这样子避免死锁
考虑下面几种错误的write端写法

void post(const Foo& f)
{
    MutexLockGuard lock(mutex);
    g_foos->push_back(f);//很明显,在读端的iter->doit()阶段,如果push_back,那么迭代器会失效
}

void post(const Foo& f)
{
    FooListPtr newFoos(new FooList(*g_foos));
    newFoos->push_back(f);
    MutexLockGuard lock(mutex);
    g_foos = newFoos;//两个线程在write期间,g_foos可能只添加了一个f
}

void post(const Foo& f)
{
    FooListPtr oldFoos;
    {
        MutexLockGuard lock(mutex);
        oldFoos = g_foos;
    }
    FooListPtr newFoos(new FooList(*oldFoos));
    newFoos->push_back(f);
    MutexLockGuard lock(mutex);
    g_foos = newFoos;//两个线程在write期间,g_foos可能只添加了一个f
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值