C++并发设计模式:资源按需创建与自动销毁问题
解决方案:线程安全的按需共享资源管理
你的核心问题在于Room类的std::shared_ptr<R> resource成员持有了一个额外的强引用,导致即使外部所有shared_ptr都释放了,引用计数仍不为0,资源要等到Room对象销毁才会被回收。我们可以通过std::weak_ptr来解决这个问题——它不会持有强引用,仅作为资源的观察者,这样只有外部返回的shared_ptr才参与引用计数。
修正后的代码
#include <iostream> #include <memory> #include <mutex> struct Lights { Lights(){ std::cout << "LIGHTS TURNED ON\n"; } ~Lights(){ std::cout << "Deleter called.\nLIGHTS TURNED OFF\n\n"; } }; template <class R> class Room { public: std::shared_ptr<R> Enter() { std::lock_guard<std::mutex> lock(resourceMutex); // 尝试从weak_ptr获取强引用 auto sp = resource.lock(); if (!sp) { // 资源已销毁或未创建,创建新实例 sp = std::make_shared<R>(); resource = sp; } else { std::cout << " ...lights already on\n"; } return sp; } private: std::mutex resourceMutex; // 使用weak_ptr替代shared_ptr,不持有强引用 std::weak_ptr<R> resource; }; int main() { Room<Lights> room; auto sp1 = room.Enter(); auto sp2 = room.Enter(); auto sp3 = room.Enter(); sp1.reset(); sp2.reset(); sp3.reset(); std::cout << "END of program.\n"; return 0; }
关键改动说明
- 替换
std::shared_ptr为std::weak_ptr:Room类的resource成员改为std::weak_ptr<R>,它不会增加资源的引用计数,仅用于观察资源是否存在。 - 通过
lock()获取强引用:每次调用Enter()时,先调用resource.lock()尝试获取有效的std::shared_ptr。如果返回空,说明资源已销毁或从未创建,此时才创建新资源。 - 简化销毁逻辑:把原自定义删除器的提示信息移到
Lights的析构函数中,依赖std::shared_ptr默认的RAII销毁机制,最后一个强引用释放时自动触发资源销毁。
输出验证
运行修正后的代码,输出将完全符合你的期望:
LIGHTS TURNED ON ...lights already on ...lights already on Deleter called. LIGHTS TURNED OFF END of program.
线程安全与特性保障
- 单实例与线程安全:
std::mutex保护了资源的创建和weak_ptr的访问过程,避免多个线程同时创建资源,保证单实例特性。 - 自动销毁:只有外部返回的
shared_ptr持有强引用,最后一个引用释放时资源立即销毁,无需等待Room对象生命周期结束。 - 可复用性:资源销毁后,
weak_ptr自动失效,下次调用Enter()会重新创建资源,满足"关闭后可再次打开"的需求,这是std::call_once无法实现的。
内容的提问来源于stack exchange,提问作者NKatUT
相关产品推荐
相关产品推荐

