“weak_ptr”和“shared_ptr”访问是原子的
How `weak_ptr` and `shared_ptr` accesses are atomic
std::shared_ptr<int> int_ptr;
int main() {
int_ptr = std::make_shared<int>(1);
std::thread th{[&]() {
std::weak_ptr int_ptr_weak = int_ptr;
auto int_ptr_local = int_ptr_weak.lock();
if (int_ptr_local) {
cout << "Value in the shared_ptr is " << *int_ptr_local << endl;
}
});
int_ptr.reset(nullptr);
th.join();
return 0;
}
上面的代码线程安全吗? 我读了这个关于weak_ptr线程安全的答案,但只是想确保上面的代码是线程安全的。
我问这个问题的原因是,如果上面的代码确实是线程安全的,我无法理解std::weak_ptr
和std::shared_ptr
接口如何使以下操作原子expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
。 在我看来,如果不使用某种互斥锁或自旋锁,就无法使上述两行逻辑代码同步。
我了解原子增量如何与共享指针的不同实例一起工作,并且我知道shared_ptr
本身不是线程安全的,但如果上述确实是线程安全的,那么它非常像线程安全shared_ptr
我不明白像上面的条件中的两行代码如何在没有锁的情况下成为原子。
上面的代码线程安全吗?
我相信不是,因为int_ptr.reset(nullptr);
正在与std::weak_ptr int_ptr_weak = int_ptr;
赛跑
我不明白 std::weak_ptr 和 std::shared_ptr 接口如何使以下操作原子
expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
这样的操作不是原子的,因为expired()
可能会返回 false,但当您对该值进行操作时,它可能不再准确。另一方面,如果它返回 true,则保证保持准确,只要从那时起没有人修改这个特定的shared_ptr实例。也就是说,对给定shared_ptr的其他副本执行的操作不会导致其过期。
weak_ptr::lock()
实现不会使用 expired()
.它可能会做一些类似原子比较交换的事情,其中只有当当前强引用的数量大于零时,才会添加一个额外的强引用。
这个问题分为两部分:
线程安全
代码不是线程安全的,但这与lock()
无关:
种族存在于int_ptr.reset();
和std::weak_ptr int_ptr_weak = int_ptr;
之间。因为一个线程正在修改非原子变量int_ptr
而另一个线程读取它,根据定义,这是数据竞赛。
所以这没关系:
int main() {
auto int_ptr = std::make_shared<int>(1);
std::weak_ptr<int> int_ptr_weak = int_ptr; //create the weak pointer in the original thread
std::thread th( [&]() {
auto int_ptr_local = int_ptr_weak.lock();
if (int_ptr_local) {
std::cout << "Value in the shared_ptr is " << *int_ptr_local << std::endl;
}
});
int_ptr.reset();
th.join();
}
示例代码的原子版本 expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
当然,整个过程不可能是原子的。真正重要的部分是,强引用计数仅在它已经大于零时才增加,并且检查和增量以原子方式发生。我不知道是否有任何特定于系统/架构的原语可用于此,但是在c ++ 11中实现它的一种方法是:
std::shared_ptr<T> lock() {
if (!isInitialized) {
return std::shared_ptr<T>();
}
std::atomic<int>& strong_ref_cnt = get_strong_ref_cnt_var_from_control_block();
int old_cnt = strong_ref_cnt.load();
while (old_cnt && !strong_ref_cnt.compare_exchange_weak(old_cnt, old_cnt + 1)) {
;
}
if (old_cnt > 0) {
// create shared_ptr without touching the control block any further
} else {
// create empty shared_ptr
}
}
不,您的代码不是线程安全的。 主线程中的int_ptr.reset()
操作(这是写入操作)与th
中从int_ptr
初始化int_weak_ptr
(这是读取操作)之间存在数据争用。
"std::weak_ptr
和 std::shared_ptr
接口如何使以下操作原子expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
"
接口没有。它是实现的内部。具体完成方式因实现而异。
- C++中原子的替代品<variant>
- 调用原子的 store() 时可以调用基类型类的函数吗?C++
- 为什么 C++20 不使用"requires"来限制原子的 T<T>?
- boost::asio::io_service::p ost 是原子的吗?
- 原子的矢量完全线程安全?
- std::包含原子的类的向量
- 初始化原子指针是原子的吗?如果初始化或内存分配引发,会发生什么情况?
- 标准::原子的锁在哪里
- stat() 相对于文件系统是原子的
- 试图最大程度地减少对每次迭代的原子的检查
- 原子的类内初始化
- 对 c++ 11 原子变量的操作实际上是原子的
- 如何配置 g++,以便 x++ 是原子的(Ubuntu,openmp)
- Qt的事件循环线程是安全的还是原子的?处理"队列连接"时如何同步?
- writev() 真的是原子的吗?
- 当某些错误可以接受时,顺序加载存储原子的内存顺序应该是什么
- “weak_ptr”和“shared_ptr”访问是原子的
- int *ptr = &var 和 int *ptr 之间的区别;*PTR = 和变量?
- 原子的正确用法
- fetch_sub真的是原子的吗