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

时间:2017-01-06 06:58:00

标签: c++ multithreading c++11 shared-ptr weak-ptr

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_ptr本身不是线程安全的,但如果上面确实是线程安全的,那就非常像线程安全{{1我不明白上面的条件中的两行代码如何在没有锁的情况下成为原子。

4 个答案:

答案 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_ptrint_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_ptrstd::shared_ptr接口如何使以下操作成为原子expired() ? shared_ptr<T>() : shared_ptr<T>(*this)&#34;

接口没有。它是实施的内部。具体如何完成它们将在实现之间有所不同。