std::weak_ptr<T>::锁定线程安全吗?
Is std::weak_ptr<T>::lock thread-safe?
下面是一些示例代码,显示了我的用例。我有一个 PIMPL,可以在其中共享实现(它只是一堆昂贵的数据),但当不再需要实现时,可以实现会被销毁。类HasImpl
的一个实例使用一个指向Impl
的共享指针,并且类定义包括一个static weak_ptr
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
,控制块是否正常? - 在另一个线程可能将 ptr 分配给空weak_ptr时调用
weak_ptr::lock
是否足够安全?也就是说,该值将返回 nullptr 还是新指针?我不在乎 nullptr 是否是虚假的(也就是说,该赋值已经发生,但其他线程还不知道)。我只是不想损坏控制块或从调用中获取无效的指针值。 - 在销毁对象的最后一shared_ptr线程时调用
weak_ptr::lock
是否安全? - 如果 1 到 3 出现问题,C++20 中的
std::atomic<std::weak_ptr<T>>
会解决问题吗?
该标准明确表示weak_ptr::lock
是"原子执行的"。所以答案 1 和 3。
对于#2,如果您询问的是分配给同一weak_ptr
,那么这是一场数据竞赛。更改共享状态use_count
的操作不会引发数据争用,但复制或操纵weak_ptr
本身不仅仅是戳use_count
。
但是,如果您谈论的是锁定一个weak_ptr
同时清空一个都与同一共享状态通信的不同weak_ptr
,那很好。两者仅通过共享状态的计数进行交互,这说明很好。
是的,atomic<weak_ptr<T>>
允许您从多个线程操作同一对象。
相关文章:
- 如何检查线程是否锁定
- 在两个线程上读/写 64 位,无互斥/锁定/原子
- 如何在实时应用程序中锁定线程
- 锁定来自其他线程的类成员
- 当只有一个线程主要使用该对象而其他线程很少使用它时,如何最小化该对象的互斥锁锁定?
- 将线程锁定很长时间
- C++将互斥锁锁定为来自另一个线程
- 在以读取为主的多线程程序中,可以使用原子来减少锁定吗
- std::weak_ptr<T>::锁定线程安全吗?
- 线程锁定互斥锁的速度比 std::conditional_variable::wait() 快
- C 优化导致线程锁定
- 是否可以在C++中使用std::atomic_flag获得线程锁定机制
- mutex::lock() 检查一次解锁状态是否已经被另一个线程锁定?
- C++11中的基本线程锁定
- boost线程锁定错误
- 当一个线程锁定一个大映射时,如何避免冻结其他线程
- 导致c++ 11 std::互斥锁将被阻塞的线程锁定到被动等待状态
- shared_ptr.get()可以被多个线程调用,而另一个线程锁定并调用shared_ptr.swap()吗?
- 发现可能由于线程锁定(可能)导致的性能问题
- C++:如何简化线程锁定