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;
}
上面的代码是否安全?我读了这个答案About thread-safety of weak_ptr,但只是想确保上面的代码是线程安全的。
我问这个的原因是,如果上面的代码确实是线程安全的,我无法理解std::weak_ptr
和std::shared_ptr
接口如何使以下操作原子expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
。在我看来,如果不使用某种互斥锁或自旋锁,就不能使上面这两行逻辑代码同步。
我理解原子增量如何与不同的共享指针实例一起工作,我理解shared_ptr
本身不是线程安全的,但如果上面确实是线程安全的,那就非常像线程安全{{1我不明白上面的条件中的两行代码如何在没有锁的情况下成为原子。
答案 0 :(得分:4)
上面的代码是否安全?
我认为不是,因为int_ptr.reset(nullptr);
正在与std::weak_ptr int_ptr_weak = int_ptr;
我无法理解std :: weak_ptr和std :: shared_ptr接口如何进行以下操作atomic
expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
这样的操作不是原子的,因为expired()
可能会返回false,但是当你对该值采取行动时,它可能不再准确。另一方面,如果它返回true,则保证保持准确,只要从那时起没有人修改这个特定的 shared_ptr实例。也就是说,对给定shared_ptr的其他副本的操作不能导致它过期。
weak_ptr::lock()
实施不会使用expired()
。它可能会执行像原子比较交换这样的事情,只有当强引用的当前数量大于零时才会添加额外的强引用。
答案 1 :(得分:3)
不,您的代码不是线程安全的。主线程中的int_ptr.reset()
操作(写入操作)与int_weak_ptr
中int_ptr
的{{1}}初始化之间存在数据争用(这是一个读取)操作)。
答案 2 :(得分:2)
这个问题分为两部分:
<强>线程安全性强>
代码不线程安全,但这与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
}
}
答案 3 :(得分:1)
&#34; std::weak_ptr
和std::shared_ptr
接口如何使以下操作成为原子expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
&#34;
接口没有。它是实施的内部。具体如何完成它们将在实现之间有所不同。