【问题标题】:Using std::weak_ptr with std::shared_ptr for shadowing使用 std::weak_ptr 和 std::shared_ptr 进行阴影
【发布时间】:2018-05-23 18:54:33
【问题描述】:

std::weak_ptron cppreference 的文档说明了这一点

有效地返回expired() ? shared_ptr<T>() : shared_ptr<T>(*this),以原子方式执行。

而且我的判断和other SO answers已经确认以下是不容易出现种族的

int main() {
    auto s_ptr = std::make_shared<T>(...);
    auto w_ptr = std::weak_ptr<T>{s_ptr};

    auto one = std::thread{[&]() {
        auto ptr = w_ptr.lock();
        if (ptr) { ... }
    }};
    s_ptr = std::make_shared<T>(...);

    one.join();
}

但是,这可以可靠地用于在程序中进行计算吗?我所说的阴影是指这样的事情

auto s_ptr = std::make_shared<T>(...);
auto w_ptr = std::weak_ptr<T>{s_ptr};

// thread one
while (...) {
    auto ptr = w_ptr.lock();
    cout << "Value contained is " << *ptr << endl;
}

// thread two
while (...) {
     // do the heavy computation on the side
     auto new_value = fetch_new_value();

     // then "atomically" swap
     s_ptr = std::make_shared<T>(std::move(new_value));
}

这里令人困惑的部分是 .lock() 返回的内容。它可以返回nullptr 吗?所有文档都说该操作将以原子方式执行。没有说明这种互斥意味着什么。 shared_ptr::operator= 中是否存在指针为空的状态? weak_ptr 可以访问此状态吗? shared_ptr::operator= on cppreference 的文档似乎没有提到这一点。

【问题讨论】:

  • 我不能引用章节,但我相信 shared_ptr 和它的小伙伴weak_ptr 是线程安全的。 auto ptr = w_ptr.lock() 确实可以返回 nullptr 如果所有 shared_ptr 都已被破坏。在我看来,shared_ptr 和 weak_ptr 是 C++11 的两个最佳特性,即使不是在线程场景中也是如此。
  • 你可能想使用这个惯用模式:if (auto ptr = w_ptr.lock()) { cout &lt;&lt; "Value contained is " &lt;&lt; *ptr &lt;&lt; endl; } else { cout &lt;&lt; "Value is dead, Jim." &lt;&lt; endl; }
  • :-) 是的,如果 share_ptr 没有被破坏是可靠的,weak_ptr 将不会返回 nullptr。 (但如果有人重构代码,这可能会成为“未来的错误”。)
  • 嗯,这里有一个更详细的答案。 • stackoverflow.com/questions/14482830/… • 关键是只有控制块本身是线程安全的。
  • 据我所知,一旦您的共享指针获得新值,您的弱指针将开始返回nullptr(因为原始对象已被破坏)。但是不会有未定义的行为(因为切换是原子的)

标签: c++ c++11 thread-safety shared-ptr weak-ptr


【解决方案1】:

一旦你的共享指针获得一个新值,你的弱指针就会开始返回nullptr,原因是一旦你的共享指针开始指向另一个对象,原始对象就会被销毁。

但不会有未定义的行为交换指针,因为切换是原子的。

(虽然,取消引用 w_ptr.lock() 返回的 nullptr 值共享指针是未定义的,并且可能会导致程序崩溃)。

每次共享指针被重置为新值时,您都需要获取一个新的弱指针。但是,到你lock() 你的弱指针时,共享指针是否仍然指向那个新值,这是任何人的猜测。

任何与控制块(引用计数)相关的东西都是原子的。所以获取一个新的弱指针是线程安全的,但不能保证指向任何东西(如果其他线程可以访问你的共享指针)。

【讨论】:

  • Galik 的回答+1。 s_ptr = make_shared&lt;T&gt;(move(new_value)); 导致 w_ptr 变为 nullptr。但是*s_ptr = new_value; 导致 w_ptr 看到了变化,但是在多线程时它使用变异剪刀(如前所述)运行。需要额外的东西来协调跨线程的突变。
【解决方案2】:

这段代码可以执行UB:

auto s_ptr = std::make_shared<T>(...);
auto w_ptr = std::weak_ptr<T>{s_ptr};

// thread one
while (...) { // A
  auto ptr = w_ptr.lock(); // B
  cout << "Value contained is " << *ptr << endl; // C
}

// thread two
while (...) {
  // do the heavy computation on the side
  auto new_value = fetch_new_value();

  // then "atomically" swap
  s_ptr = std::make_shared<T>(std::move(new_value)); // X
}

如果线程二在线程一执行// B之前执行// X,则weak_ptr不再引用任何数据。

.lock() 然后返回一个“null”shared_ptr,您可以在 // C 继续取消引用。

如果您避免使用 ptr 以防止其为空,则不会出现明显的 UB。

【讨论】:

  • 知道了,我原来误解了shared_ptrweak_ptr之间的关系。这种关系不是与shared_ptr 对象,而是与控制块。我以前从未使用过weak_ptr,并认为我有一个很好的用例。
猜你喜欢
  • 2016-07-31
  • 1970-01-01
  • 2013-08-18
  • 2014-12-17
  • 2015-08-25
  • 2016-08-03
  • 2023-03-17
  • 1970-01-01
  • 2012-05-13
相关资源
最近更新 更多