下面是一些显示我的用例的示例代码。我有一个 PIMPL,可以在其中共享实现(它只是一堆昂贵的数据),但是当不再需要时,可以销毁实现。该类的一个实例HasImpl
使用一个指向 的共享指针Impl
,并且该类定义包括一个static weak_ptr
to 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;
}
}
- 假设它不是空的并且在遥远的过去某个时候被分配了一个指针,如果一个线程调用而另一个线程可能正在调用
std::weak_ptr
,控制块会好吗?weak_ptr::lock
weak_ptr::lock
weak_ptr::lock
在另一个线程可能将 ptr 分配给空的 weak_ptr 时调用是否足够安全?也就是说,该值将返回 nullptr 还是返回新指针?我不在乎 nullptr 是否是虚假的(也就是说,该分配已经发生,但其他线程还不知道)。我只是不想破坏控制块或从调用中获取无效的指针值。weak_ptr::lock
在对象的最后一个 shared_ptr 被销毁时调用线程安全吗?- 如果 1 到 3 有问题,
std::atomic<std::weak_ptr<T>>
C++20 会解决这个问题吗?