`weak_ptr`和`shared_ptr`访问如何是原子的

Cur*_*ous 6 c++ multithreading shared-ptr weak-ptr c++11

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;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码是否安全?我读了这个答案关于weak_ptr的线程安全,但只是想确保上面的代码是线程安全的.

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

我理解原子增量如何与不同的共享指针实例一起工作,我理解它们shared_ptr本身不是线程安全的,但如果上面确实是线程安全的,那就非常像线程安全shared_ptr而且我不明白两行代码如何就像在条件上面可以做成原子而没有锁.

Jos*_*ich 5

上面的代码线程安全吗?

我相信这不是,就像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(). 它可能会执行类似于原子比较交换的操作,其中仅当当前强引用数量大于零时才会添加额外的强引用。

  • 您对“weak_ptr::lock()”的使用确实是线程安全的,但是在从强指针构造弱指针和重置该强指针之间存在不相关的竞争条件。 (3认同)
  • @Curious:“weak_ptr::lock()”操作相对于“shared_ptr”和“weak_ptr”的其他实例上的并发操作是安全的,这些实例与您调用“lock()”的“weak_ptr”共享状态。此类操作还包括销毁另一个实例。 (2认同)

Mik*_*eMB 4

这个问题有两个部分:

线程安全

该代码不是线程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();
}
Run Code Online (Sandbox Code Playgroud)

示例代码的原子版本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
    }
}
Run Code Online (Sandbox Code Playgroud)