// 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,如果是这样我应该怎么做?
我对你如何最终选择这个设计感到难以置信......我希望你的真实项目有意义。
你应该将指针(或最好是一个引用)传递给shared_ptr
的唯一原因是重新安置它(正如你所做的那样)并且你不需要同步交换本身(shared_ptr
已经同步)。
但是,交换内容会导致与当时使用对象(读取或写入)的其他线程发生同步问题。因此,如果两个线程都在修改对象,则需要应用相同的同步技术。
这对我来说并不像是对任何实际问题的强大解决方案。也许考虑将一个新的std::shared_ptr
传递给另一个线程,然后发信号通知它,以便它知道拿起新的共享指针(通过复制而不是指针)。
与(某些)传统智慧相反,shared_ptr
对象本身不是线程安全的。也就是说,你不能同时在不同的线程上操作一个shared_ptr
对象,比你可以同时操作std::vector
或std::string
或任何其他具有“默认”线程安全性的标准库对象。
引起混淆的原因是典型的shared_ptr
确实使用原子操作来操纵其引用计数 - 但这只是为了支持不同的shared_ptr
对象指向同一个底层对象(因此共享引用计数)的场景。
这种隐藏共享与以前在std::string
的COW实现中发生的事情类型相同:你无法安全地操纵不同线程上的相同std::string
对象而没有锁定,但COW行为意味着没有原子操作你甚至无法做到操纵两个不同的std::string
对象碰巧在不同的线程上共享相同的底层缓冲区,违反了std::string
对象的值语义和标准库线程安全保证。因此在实践中,您使用原子操作来操作COW缓冲区引用计数,但其他操作通常以非线程安全的方式编写。
所以最重要的是,不仅可能是一个糟糕的设计来共享这样的shared_ptr
:明确不允许它,因为它不是线程安全的。
1此处同时操作意味着在多个线程上访问shared_ptr
对象,其中至少一些访问是非const
方法。标准库提供了一般保证,即仅包含const
方法访问的并发访问是安全的(即,const
用作从线程角度看操作是“只读”的标志)。