将指针传递给shared_ptr线程安全

时间:2018-01-03 18:02:05

标签: c++ multithreading thread-safety shared-ptr stdatomic

// Example program
#include <iostream>
#include <string>
#include <memory>
#include <atomic>

struct Smth {};

struct B { //Used in thread 2
    B(std::shared_ptr<Smth>* smth) : smth_(smth) {};
    ~B();
    void DoSomething() {
        std::shared_ptr<Smth> newSmth = std::make_shared<Smth>();
        smth_->swap(newSmth);
    }
    std::shared_ptr<Smth>* smth_;
};

struct A { //Used in thread 1&3
  A() : smth_(std::make_shared<Smth>()), b_(&smth_) {};
  ~A();
  std::shared_ptr<Smth> smth_;
  B b_;
};

将指针传递给shared_ptr以便我可以在单独的线程中交换shared_ptr的内容是一种好习惯吗? 第二个问题是我应该使用std :: atomic_store来替换shared_ptr :: swap,如果是这样我应该怎么做?

2 个答案:

答案 0 :(得分:1)

我的想法令人难以置信,你可能最终选择了这个设计......我希望你的真实项目有意义。

因为你应该将指针(或最好是引用)传递给shared_ptr的原因是重新安置它(就像你正在做的那样)并且你没有&#39;需要同步 swap 本身(shared_ptr已经同步)。

但是,交换内容会导致与使用该对象(读取或写入)的其他线程同步问题。因此,如果两个线程都在修改对象,则需要应用相同的同步技术。

对我而言,这并不是一个能够解决您实际问题的强大解决方案。也许考虑将新的std::shared_ptr传递给另一个线程,然后发送信号,以便它知道拿起新的共享指针(通过复制而不是指针)。

答案 1 :(得分:1)

与(某些)传统智慧相反,shared_ptr对象本身不是线程安全的。也就是说,您不能同时在不同的线程上操作 1 单个shared_ptr对象,而不能同时操作std::vectorstd::string或任何其他标准库对象带&#34;默认&#34;线程安全。

出现混淆是因为典型的shared_ptr确实使用原子操作来操纵其引用计数 - 但这只是为了支持不同的 shared_ptr个对象的场景指向相同的底层对象(因此共享引用计数)。

这种隐藏共享与以前在std::string的COW实现中发生的事物类型相同:您无法在不锁定的情况下安全地操纵不同线程上的相同std::string对象,但是COW行为意味着如果没有原子操作,你甚至无法操纵两个不同的 std::string对象,它们恰好在不同的线程上共享相同的底层缓冲区,违反了{的语义。 {1}}对象和标准库线程安全保证。所以在实践中,你有原子操作来操作COW缓冲区引用计数,但是其他操作像往常一样以非线程安全的方式编写。

所以最重要的是,不仅可能是一个糟糕的设计来共享这样的std::string:明确不允许它,因为它不是线程安全的。

1 此处同时操作意味着访问多个线程上的shared_ptr个对象,其中至少一些访问属于非shared_ptr方法。标准库提供了一般保证,即{em>仅 const - 方法访问的并发访问是安全的(即const用作操作的标志&#34 ;从线程角度来看,只读&#34;