`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;
}

上面的代码线程安全吗?我读了这个答案 About thread-safety of weak_ptr 但只是想确保上面的代码是线程安全的。

我问这个的原因是,如果上面的代码确实是线程安全的,我无法理解 std::weak_ptrstd::shared_ptr 接口如何使以下操作成为原子操作 expired() ? shared_ptr<T>() : shared_ptr<T>(*this)。在我看来,如果不使用某种互斥锁或自旋锁,就不能使像上面这样的两行逻辑代码同步。

我了解原子增量如何与共享指针的不同实例一起工作,并且我了解 shared_ptrs 本身不是线程安全的,但如果以上内容确实是线程安全的,那么它非常像线程安全 shared_ptr 而且我不明白上面条件中的两行代码如何在没有锁的情况下成为原子的。

std::weak_ptrstd::shared_ptr 接口如何使以下操作成为原子操作 expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

接口没有。它是实施的内部。具体的实现方式因实现而异。

Is the code above thread safe?

我相信不是,因为 int_ptr.reset(nullptr); 正在与 std::weak_ptr int_ptr_weak = int_ptr;

比赛

I cannot understand how the std::weak_ptr and std::shared_ptr interfaces make the following operation atomic expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

这样的操作不是原子操作,因为 expired() 可能 return 错误,但当您根据该值采取行动时,它可能不再准确。另一方面,如果它 return 是真的,那保证保持准确,只要从那时起没有人修改 这个特定的 shared_ptr 实例。也就是说,对给定 shared_ptr 的其他副本的操作不会导致它过期。

weak_ptr::lock() 实现不会使用 expired()。它可能会做一些类似原子比较交换的事情,只有当当前的强引用数量大于零时,才会添加一个额外的强引用。

不,您的代码不是线程安全的。主线程中的 int_ptr.reset() 操作(写操作)和 th 中的 int_ptrint_weak_ptr 初始化(读操作)之间存在数据竞争操作)。

这个问题分为两部分:

线程安全

该代码不是线程安全的,但这与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)

当然不可能整个过程都是原子的。实际上重要的部分是强引用计数仅在它已经大于零时才增加,并且检查和增加以原子方式发生。我不知道是否有任何 system/architecture 特定原语可用于此,但在 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
    }
}