【问题标题】:Is it safe to return by value a shared_ptr that is guarded by mutex?按值返回由互斥锁保护的 shared_ptr 是否安全?
【发布时间】:2024-01-18 02:47:01
【问题描述】:

这是一个代码示例:

class A {
  boost::mutex a_mutex;
  boost::shared_ptr<int> a;

  boost::shared_ptr<int> clone_a(void) {
    boost::lock_guard<boost::mutex> lock(a_mutex);
    return a;
  }
};

建议是 A::a 上的 boost::shared_ptr 复制构造函数调用将在 boost::lock_guard 析构函数调用之前,尽管编译器进行了优化。 那么,拨打A::clone_a() 安全吗?

【问题讨论】:

标签: c++ gcc shared-ptr


【解决方案1】:

如果“安全”是指您不会在 a 上进行数据竞争,那么可以。确实如你所说。

但是,您可能知道,它不会保护对*a(或*clone_a())的进一步访问。我不确定,为什么该方法称为“克隆”,因为它不会克隆任何东西。

【讨论】:

    【解决方案2】:

    是的,这段代码是安全的。如果您参考shread_ptr Thread-Safety,您可以看到对线程本地 shared_ptr 对象的线程写访问就可以了。

    在上面的代码中,访问成员 shared_ptr 需要锁,因为它可以被多个线程访问。到临时返回的复制是在锁内完成的,因此你在那里是安全的。其他线程看不到该临时文件,因此您可以安全地将其复制到其他 shared_ptr。

    现在可能选择clone_a 作为函数名是错误的。您没有克隆底层对象,您只是获取 shared_ptr 的副本。所以我假设你打算共享相同的底层“int”。

    【讨论】:

      【解决方案3】:

      如果您使用返回值,则不会。返回值本身是一个 临时的,其生命周期超出函数的末尾;它 将在调用的完整表达式的末尾被破坏 A::clone_a。所以如果你写这样的东西:

      shared_ptr<int> newA = object->clone_a();
      

      ,形式语义将是临时返回的 object-&gt;clone_a() 被复制到 newA,在上下文中 调用者(因此不受互斥锁的保护)。在这种特殊情况下,您 可能会因为 RVO 而侥幸逃脱,但这不一定是 情况下,还有其他 RVO 无法干预的情况。

      如果您只担心指针的副本,我很确定 如果您设置正确的编译器选项 (-D somthing),boost::shared_ptr 将自动运行。在 在这种情况下,您根本不需要互斥锁。

      【讨论】:

      • 但这就是问题所在:受到什么保护?传递和复制 shared_ptr 是完全安全的,即使是临时的,每个都正确维护引用计数。也就是说,您的newA 保证指向锁定段中引用的同一对象。
      • @edA-qamort-ora-y A::clone_a() 应该创建一个特定于线程的共享指针,因此它不能被另一个线程更改。
      • @James,abyss 是正确的,在锁之外复制的 shared_ptr 是调用线程的本地,不需要保护。因此,此代码如图所示是安全的。
      • 我理解,底层引用计数始终是线程安全的。包装 shared_ptr 不是,因此需要由互斥锁保护。所以一旦你复制到一个线程本地 shared_ptr 你是安全的,因为没有其他线程可以引用这个包装器。
      • @James,不,这是不正确的。有一个选项可以使它成为非线程安全的,但在正常模式下它是线程安全的。而且我看不到让它超级安全的选项,在上面的函数中总是需要互斥锁(假设调用它的代码是多线程的)。