【问题标题】:Returning shared_ptr and exception safety返回 shared_ptr 和异常安全
【发布时间】:2016-08-05 13:53:42
【问题描述】:

我正在阅读“C++ Concurrency in action”一书并试图理解线程安全数据结构(例如堆栈)中的异常安全性。作者指出,为了避免竞争条件,pop 应该同时执行这两项操作 - 从堆栈中弹出和返回项目,但是:

如果 pop() 函数被定义为返回弹出的值,并将其从堆栈中移除,那么您就有一个潜在的问题:只有在堆栈被修改后,被弹出的值才会返回给调用者,但是复制数据以返回给调用者的过程可能会引发异常。

这里是建议的解决方案:

std::shared_ptr<T> pop()
{
    std::lock_guard<std::mutex> lock(m);
    if(data.empty()) throw runtime_error("empty");
    std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
    data.pop();
    return res;
}

在这种情况下,我们在make_shared 行调用了复制构造函数。如果复制构造函数抛出异常,堆栈还没有被修改,我们很好。

但是我看不出它与这个 sn-p 有什么不同:

T pop2() {
    std::lock_guard<std::mutex> lock(m);
    if(data.empty()) throw runtime_error("empty");
    auto res = data.top();
    data.pop();
    return res;
}

这里我们在data.top() 行调用复制构造函数,返回时移动构造函数。同样,如果复制构造函数抛出异常,我们很好,因为堆栈尚未修改。移动构造函数不应该抛出异常。

我错过了什么吗?与返回(可移动)T 相比,返回 shared_ptr 有什么好处?

【问题讨论】:

  • 移动构造函数允许抛出。
  • 我这本书的 PDF 副本有 throw empty_stack() 而不是 throw runtime_error("empty")。书中的empty_stack类直接继承自std::exception。如果您问题中的runtime_errorstd::runtime_error,请注意throw runtime_error("empty"); might throw other exceptions as well
  • 是的,我的也有empty_stack。我只是在示例中将其替换为runtime_exception
  • 请注意,resconst 会阻止移动操作并将强制复制(除非发生复制省略,这不能保证)。

标签: c++ c++11 stack shared-ptr move-semantics


【解决方案1】:

如果T 不可移动,您的代码最终可能会执行多次复制构造。在这种情况下,使用shared_ptr 在堆上分配副本可能更有效。

即使T 是可移动的,您的代码仍可能会执行多个(可能代价高昂的)移动构造。已知移动构造和/或复制构造 shared_ptr 与移动或复制构造任何可能的类型 T 相比相对便宜。

当然,根据具体类型、编译器以及操作环境和硬件,您的实际使用量可能会有所不同。

【讨论】:

  • 因此,假设 T 是可移动的并且移动 constructor 不会引发异常,返回 shared_ptr 的好处将是节省 move constructor 调用而没有任何作用与异常安全有关?
  • 是的,我想是的。更准确地说,它保存了T 的一个移动构造,但添加了shared_ptr&lt;T&gt; 的移动。第二个区别是std::make_sharedheap中分配值,而在pop2()中你在stack上构造并返回值,这可能也有一些性能与内存局部性有关的含义。
【解决方案2】:

move-constructor 可能会抛出(通常)。 std::shared_ptr&lt;T&gt; 有一个 noexcept 移动构造函数。

所以在第一种情况下return res; 不能抛出,但在第二种情况下return res; 可能抛出(并且data.pop() 已经被调用)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-22
    • 1970-01-01
    • 2020-03-09
    • 1970-01-01
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多