通过 std::shared_ptr<T>::reset() 进行同步销毁

Synchronous destruction through std::shared_ptr<T>::reset()

本文关键字:同步 reset shared std ptr 通过 lt gt      更新时间:2023-10-16

考虑以下简化的程序建模一个真实的场景,其中不同的用户可以并发请求相同的资源:

#include <thread>
#include <memory>
#include <mutex>
#include <iostream>
using namespace std;
struct T {
    void op() { /* some stuff */ }
    ~T() noexcept { /* some stuff */ }
};
std::shared_ptr<T> t;
std::mutex mtx;
std::weak_ptr<T> w{t};
enum action { destroy, op};
void request(action a) {
    if (a == action::destroy) {
        lock_guard<mutex> lk{mtx};
        t.reset();
        std::cout << "*t certainly destroyedn";
    } else if (a == action::op) {
        lock_guard<mutex> lk{mtx};
        if (auto l = w.lock()) {
            l->op();
        }
    }
}
int main() {
    // At some point in time and different points in the program,
    // two different users make two different concurrent requests
    std::thread th1{request, destroy}; std::thread th2{request, op};
    // ....
    th2.join();
    th1.join();
}
我并不是在问程序是否在形式上是正确的——我认为它是正确的,但是我从来没有见过这种方法保证通过智能指针共享的资源的同步销毁。我个人认为它很好,有一个有效的用途。

然而,我想知道其他人是否也这么想,如果有的话,除了与unique_lock s和条件变量的经典同步以及引入对T的修改(例如原子标志)之外,是否有更优雅的替代方案。

如果我能以某种方式摆脱mtx,那将是理想的。

很好。shared_ptr中的引用计数是原子计数,并且锁定的副本在操作期间保持在作用域中,因此在操作期间不能销毁对象。

在这种情况下,互斥锁实际上并没有保护T的生命周期,而是对op()的调用和销毁进行排序。如果你不介意多个并发调用op(),或者销毁时间不确定(即在最后运行的op()完成之后),那么你可以取消它,因为std::shared_ptr<>::reset()std::weak_ptr<>::lock()都是线程安全的。

然而,我建议谨慎,因为作者明确表示要序列化对op()的调用。