shared_ptr和const方法线程安全

时间:2016-05-10 17:04:47

标签: c++ c++11 thread-safety shared-ptr

假设我们有class A个const方法(仅限)。

class A
{
public:
    void method1() const;
    void method2() const;
}

此外,我们还有另一个class B shared_ptr class A以及返回它的方法,并使用互斥保护进行更改:

class B
{
    std::shared_ptr<A> ptr;
    std::mutex m;
public:
    B()
    {
        ptr = std::make_shared<A>();
    }

    std::shared_ptr<A> getPtr() const
    {
        mutex_on_stack mut(m);
        return ptr;
    }

    void resetPtr()
    {
        mutex_on_stack mut(m);
        ptr.reset(new A());
    }
}

ptr变量受互斥锁保护,因此我认为它是线程安全的。但我不确定内在物体本身的安全性。

B objB;

//thread 1
auto p1 = objB->getPtr();
p1->method1(); //calling const method

//thread 2
auto p2 = objB->getPtr();
p2->method2(); //calling const method

//thread 3
objB->resetPtr();

线程1和2调用getPtr()方法并在复制时增加shared_ptr的引用计数器。所以我认为内部指针不会被线程3中的resetPtr()意外删除。此外,方法method1method2是const,因此它不会修改内部对象。但我错了。

所以我有两个问题:

  1. 上面的代码是否是线程安全的?
  2. class A线程安全吗?

1 个答案:

答案 0 :(得分:1)

A是线程安全的,因为它只使用const函数,可以假设它们不会改变外部可见状态。虽然const函数能够修改内部状态,如果该类也有mutable成员,但通常假设任何mutable成员都是同步的,因此两个并发读者一个物体永远不会遇到竞争条件。

访问shared_ptr的内容是线程安全的,实际上甚至不需要互斥锁的额外同步。来自cppreference

  

所有成员函数(包括复制构造函数和复制赋值)   可以由shared_ptr的不同实例上的多个线程调用   即使这些实例是副本,也没有其他同步   并分享同一对象的所有权。

但是,只有当您未调用相关const的非shared_ptr函数时,此操作才有效:

  

如果多个执行线程没有访问相同的shared_ptr   同步和任何这些访问使用非const成员   shared_ptr的功能然后会发生数据竞争; shared_ptr   原子函数的重载可用于防止数据竞争。

因此,如果没有互斥锁,那么类B现在就不会是完全线程安全的,因为你在函数上调用了reset。如果不使用atomic overloads for shared_ptr调用reset而不使用互斥锁,它将成为线程安全的。

实际上,确保线程安全所需要做的唯一事情就是确保你的RAII包装器一直持续到功能范围的结束,它正在进行。

关于RAII包装器的主题,您不需要推出自己的锁定解锁机制。标准库已经为您提供了RAII锁,其形式为lock_guardunique_lockshared_lock。像这样的简单场景可能与lock_guard最匹配,<fieldset data-role="controlgroup" id="pilihtambahan" style="margin-top: 5px;"> </fieldset> $.ajax({ url: host+'/skripsi3/phpmobile/tambahbarang.php', data: { "id": getacara}, dataType: 'json', success: function(data, status){ console.log(data); $.each(data, function(i,item){ $("#pilihtambahan").append('<input class="tambahsewa" type="checkbox" value="'+item.idbarang+'" name="cektambah" id="'+item.idbarang+'"><label for="'+item.idbarang+'">'+item.namabarang+' @ Rp.'+item.harga+',-</label><input type="text" id="'+item.idbarang+'" name="jumsewa" placeholder="Jumlah Sewa '+item.namabarang+'"><hr>').trigger("create"); }); }, error: function(){ console.log("error"); //output.text('There was an error loading the data.'); } }); 旨在用作简单的作用域锁。