通过std :: shared_ptr <t> :: reset()

时间:2016-09-21 20:45:46

标签: c++ c++11 boost c++14

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

#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 destroyed\n";
    } 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和条件变量的经典同步以及将修改(例如原子标志)引入{之外还有更优雅的替代方案{1}}。

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

1 个答案:

答案 0 :(得分:1)

是的,没关系。 shared_ptr中的引用计数是原子的,并且锁定的副本在op的持续时间内保持在范围内,因此在op期间不能销毁该对象。

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

但是,我建议谨慎,因为作者明确要求将op()的来电序列化。