在第一个线程到达时构造资源并在最后一个线程离开时销毁的设计模式

问题描述 投票:0回答:0

我正在寻找 C++ 中的并发设计模式,它使单个资源实例能够由需要它的第一个线程构造,只要至少有一个线程正在访问它(即使它不是创建线程),它就会持续存在),并在最后一个访问它的线程完成时自动销毁(即当对资源的最后一次引用超出范围时以 RAII 方式)。

一个实际的类比是第一个进入房间的人(线程)如何打开灯(构建资源)和最后一个离开房间的人如何关闭灯(破坏资源)。只要房间里至少有一个人,灯就会一直亮着(持久性)。灯一旦打开就无法打开(单个资源实例)。两个或更多人(单独的线程)不能同时打开或关闭开关(线程安全)。在之前的开关循环中已经关闭后,灯可以重新打开(多次使用,而不是 call_once)。

下面我最初的尝试是在一个由互斥锁保护的工厂方法中使用 std::shared_ptr 的引用计数特性,但我似乎无法让它的删除部分在正确的时间工作并在需要时销毁我的资源。

这是我的初学者代码(但我承认我可能偏离了方向并且错过了一些其他常见且优雅的模式/解决此问题的方法)。谢谢!

#include <iostream>
#include <memory>
#include <mutex>

struct Lights {
    Lights(){ std::cout << "LIGHTS TURNED ON\n"; }
    ~Lights(){ std::cout << "LIGHTS TURNED OFF\n\n"; }
};

template <class R>
class Room {
public:
    std::shared_ptr<R> Enter() {   // factory method
        std::lock_guard<std::mutex> lock(resourceMutex);
        if (!resource) {
            resource = std::shared_ptr<R>(new R, [this](R* ptr){ Deleter(ptr); });
        }
        else {
            std::cout << "  ...lights already on\n";
        }

        // Copy the shared_ptr (increase the reference count)
        return resource;
    }

private:
    std::mutex resourceMutex;
    std::shared_ptr<R> resource{nullptr};

    void Deleter(R* ptr){
        std::lock_guard<std::mutex> lock(resourceMutex);
        std::cout << "Deleter called.\n";
        delete ptr;
    }
};

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;
}

产生输出:

LIGHTS TURNED ON
  ...lights already on
  ...lights already on
END of program.
Deleter called.
LIGHTS TURNED OFF

而我想要的输出是:

LIGHTS TURNED ON
  ...lights already on
  ...lights already on
Deleter called.
LIGHTS TURNED OFF
END of program.

也就是说,我希望在

sp3.reset()
调用后关闭灯,而不是当
room
对象在
main()
结束时离开范围。对于这种方法,我需要一种聪明的方法来避免
room
类内部的额外引用计数(保存在它的
resource
成员中),而是只有从
Enter()
返回并存储在
sp1 中的智能指针
,
sp2
,
sp3
有助于引用计数。然而,每次调用
Enter()
时,工厂仍然必须能够复制相同的 shared_ptr。

(注意,我意识到 main() 中的用法没有显示任何线程测试,为了简单起见,我删除了它,因为 shared_ptr 引用数量的主要问题可以用如图所示的单个线程来演示。我相信删除器中的互斥锁可以防止灯在并发线程打开的同时关闭。)

c++ multithreading design-patterns shared-ptr reference-counting
© www.soinside.com 2019 - 2024. All rights reserved.