将指针传递给shared_ptr线程安全

问题描述 投票:2回答:2
// 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,如果是这样我应该怎么做?

c++ multithreading thread-safety shared-ptr stdatomic
2个回答
1
投票

我对你如何最终选择这个设计感到难以置信......我希望你的真实项目有意义。

你应该将指针(或最好是一个引用)传递给shared_ptr的唯一原因是重新安置它(正如你所做的那样)并且你不需要同步交换本身(shared_ptr已经同步)。

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

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


1
投票

与(某些)传统智慧相反,shared_ptr对象本身不是线程安全的。也就是说,你不能同时在不同的线程上操作一个shared_ptr对象,比你可以同时操作std::vectorstd::string或任何其他具有“默认”线程安全性的标准库对象。

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

这种隐藏共享与以前在std::string的COW实现中发生的事情类型相同:你无法安全地操纵不同线程上的相同std::string对象而没有锁定,但COW行为意味着没有原子操作你甚至无法做到操纵两个不同的std::string对象碰巧在不同的线程上共享相同的底层缓冲区,违反了std::string对象的值语义和标准库线程安全保证。因此在实践中,您使用原子操作来操作COW缓冲区引用计数,但其他操作通常以非线程安全的方式编写。

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


1此处同时操作意味着在多个线程上访问shared_ptr对象,其中至少一些访问是非const方法。标准库提供了一般保证,即仅包含const方法访问的并发访问是安全的(即,const用作从线程角度看操作是“只读”的标志)。

© www.soinside.com 2019 - 2024. All rights reserved.