1

下面是一些显示我的用例的示例代码。我有一个 PIMPL,可以在其中共享实现(它只是一堆昂贵的数据),但是当不再需要时,可以销毁实现。该类的一个实例HasImpl使用一个指向 的共享指针Impl,并且该类定义包括一个static weak_ptrto Impl,它充当指向 的新实例的“指针分配器” HasImpl,为它们提供一个句柄,以表明Impl是否已经存在。

该示例有两种调用weak_ptr::lock方式——一种假设下面问题 1-3 的答案都是“是”,另一种则不是。我更喜欢weak_ptr::lock线程安全的唯一原因是可能有多个线程试图获取指向的指针的副本Impl,如果lock是线程安全的,则大多数执行线程不必传递静态变量定义(线程必须检查它是否已经初始化)并且不必竞争获取互斥锁。

/* In HasImpl.h */
class HasImpl {
public:
  HasImpl();
private:
  class Impl;
  static std::weak_ptr<Impl> sharedImplDispenser;
  std::shared_ptr<Impl> myPtrToSharedImpl;
}

/* In HasImpl.cpp */
class HasImpl::Impl {
public:
  Impl(); //constructor that takes a lot of time to run
  //Lots of stuff, expensively produced, accessable to HasImpl through a shared_ptr to Impl
}

/* hypothetical constructor if weak_ptr::lock is thread-safe */
HasImpl::HasImpl() : myPtrToSharedImpl{sharedImplDispenser.lock()}
{
  if (!myPtrToSharedImpl) {
    static std::mutex mtx;
    std::lockguard<std::mutex> lck(mtx);
    myPtrToSharedImpl = sharedImplDispenser.lock();
    if (!myPtrToSharedImpl) {
      const auto new_impl{std::make_shared<Impl()};
      sharedImplDispenser = new_impl; // the only place in the program where a value is assigned to sharedImplDispenser
      myPtrToSharedImpl = new_impl;
    }
  }
}

/* hypothetical constructor if weak_ptr::lock is not thread-safe */
HasImpl::HasImpl()
{
  static std::mutex mtx;
  std::lockguard<std::mutex> lck(mtx);
  myPtrToSharedImpl = sharedImpl.lock();
  if (!myPtrToSharedImpl) {
    const auto new_impl{std::make_shared<Impl()};
    sharedImplDispenser = new_impl; // the only place in the program where a value is assigned to sharedImplDispenser
    myPtrToSharedImpl = new_impl;
  }
} 
  1. 假设它不是空的并且在遥远的过去某个时候被分配了一个指针,如果一个线程调用而另一个线程可能正在调用std::weak_ptr,控制块会好吗?weak_ptr::lockweak_ptr::lock
  2. weak_ptr::lock在另一个线程可能将 ptr 分配给空的 weak_ptr 时调用是否足够安全?也就是说,该值将返回 nullptr 还是返回新指针?我不在乎 nullptr 是否是虚假的(也就是说,该分配已经发生,但其他线程还不知道)。我只是不想破坏控制块或从调用中获取无效的指针值。
  3. weak_ptr::lock在对象的最后一个 shared_ptr 被销毁时调用线程安全吗?
  4. 如果 1 到 3 有问题,std::atomic<std::weak_ptr<T>>C++20 会解决这个问题吗?
4

1 回答 1

5

该标准明确表示这weak_ptr::lock是“原子执行”。所以答案是 1 和 3。

对于#2,如果您要询问分配给same weak_ptr,那么这是一场数据竞赛。更改共享状态的use_count操作不会引发数据竞争,但复制或操作weak_ptr自身所做的不仅仅是戳戳use_count

但是,如果您正在谈论锁定一个weak_ptr同时取消另一个weak_ptr都与相同共享状态交谈的不同,那很好。两者仅通过共享状态的计数进行交互,据说这很好。

是的,atomic<weak_ptr<T>>将允许您从多个线程操作相同的对象。

于 2018-07-28T03:00:10.890 回答