返回包含std :: mutex锁的RAII容器类

时间:2018-05-25 15:17:12

标签: c++ multithreading

我想用一个互斥锁包装一个类实例的所有用法。今天我有

std::map<int, std::shared_ptr<MyClass>> classes;

以及查找和返回实例的函数,例如:

std::shared_ptr<MyClass> GetClass(int i);

我想确保GetClass()只能在其他人尚未检索到实例的情况下检索实例,并使用一些RAII机制。用法如下:

void CallingFunction()
{
    auto c = GetClass(i); // mutex for class id 'i' is acquired here

    // some calls to class
    c.SomeFunction();
} // mutex is released here when 'c' goes out of scope

使用CallingFunction()获取的互斥锁,其他想要访问同一个类实例的线程会阻止对GetClass()的调用。

我一直在寻找一些方法,例如使用包装类,如:

class ClassContainer
{
    public:
        std::shared_ptr<Class> c;
        std::mutex m;
};

我将GetClass()修改为:

ClassContainer GetClass(int i);

但是我无法弄清楚std :: mutex应该保留在哪里,我尝试将它存储在地图中,然后再转向使用容器类:

std::map<int, std::pair<std::mutex, std::shared_ptr<MyClass<>>> classes;

但是效果不好,现在使用ClassContainer如何让ClassContainer锁定std :: mutex,如std :: lock_guard&lt;&gt;当调用者通过调用GetClass()获得一个时。

3 个答案:

答案 0 :(得分:2)

  

我一直在寻找一些方法,例如使用包装类,如:

是的,这是正确的方法并且你很接近,但是你不能将mutex本身保留在这个类中,只有锁定器。并且std::unique_lock是适合的类型,因为它必须移动ctor等。我会将字段设为私有,并创建必要的访问者:

class ClassContainer
{
    std::shared_ptr<Class> c;
    std::uniqe_lock<mutex> lock;
public:
    ClassContainer( std::pair<std::mutex,std::shared_ptr<Class>> &p ) :
        c( p.second ),
        lock( p.first ) 
    {
    }
    Class * operator->()const { return c.get(); }
    Class & operator*() const { return *c; }
};

然后用法很简单:

void CallingFunction()
{
    auto c = GetClass(i); // mutex for class id 'i' is acquired here

    // some calls to class
    c->SomeFunction();
    // or even
    GetClass(i)->SomeFunction();
} 

答案 1 :(得分:1)

Class应保留mutex,例如:

class Class
{
public:

    // Your methods...

    std::mutex& GetMutex() { return m; }
private:
    std::mutex m;
};

class ClassContainer
{
public:
    ClassContainer(std::shared_ptr<Class> c) :
         c(std::move(c)),
         l(this->c->GetMutex())
    {}

    ClassContainer(const ClassContainer&) = delete;
    ClassContainer(ClassContainer&&) = delete;
    ClassContainer& operator =(const ClassContainer&) = default;
    ClassContainer& operator =(ClassContainer&&) = default;

    // For transparent pointer like access to Class.
    decltype(auto) operator -> () const { return c; }
    decltype(auto) operator -> () { return c; }

    const Class& operator*() const { return *c; }
    Class& operator*() { return *c; }

private:
    std::shared_ptr<Class> c;
    std::lock_guard<std::mutex> l;
};

ClassContainer GetClass(int i)
{
    auto c = std::make_shared<Class>();
    return {c}; // syntax which avoids copy/move contructor.
}

最后用法:

auto&& cc = GetClass(42); // `auto&&` or `const&` pre-C++17, simple auto possible in C++17

cc->ClassMethod();

Simplified demo

答案 2 :(得分:0)

不小心,我最近做了一些非常相似的事情(只是我返回了对象而不是shared_ptr的引用。代码的作用如下:

struct locked_queue {
    locked_queue(locked_queue&& ) = default;
    mutable std::unique_lock<decltype(queue::mutex)> lock;
    const queue::q_impl_t& queue; // std::deque
};

以下是它的使用方法:

locked_queue ClassX::get_queue(...) {
    return {std::unique_lock<decltype(mutex)>{mutex}, queue_impl};
}