在类中正确使用mutex,lock_guard,unique_lock

时间:2018-04-03 18:18:48

标签: c++ multithreading locking mutex c++17

在使用课程时,我试图更好地理解std::mutexstd::lock_guardstd::unique_lock

对于初学者我有点了解lock_guardunique_lock之间的区别:我知道lock_guard只锁定构造上的互斥锁,这在类成员中使用时是首选用途功能如下:

class Foo {
    std::mutex myMutex;

public:
    void someFunc() {
        std::lock_guard<std::mutex> guard( myMutex );

        // code
    }
};

如上所述,具有类成员myMutex的lock_guard将被锁定在函数Foo::someFunc()范围的开头,然后在代码离开作用域后由于lock_guard的析构函数而解锁和mutex

我也明白unique_lock允许您锁定&amp;多次解锁互斥锁。

我的问题是关于设计课程的地方;如果我想将互斥锁锁定在类的构造函数中,那么当constructor超出范围时不要解锁它,而是在调用类的析构函数时解锁...

class Foo {
    std::mutex myMutex;
public:
    Foo() {
        // lock mutex here;
    }

    ~Foo() {
        // unlock mutex here;
    }
};

可以实现上述目标;如果是这样的话?

在上面的最后一个例子中,我不确定的是:如果在类的构造函数中使用了lock_guard;它会在构造函数离开作用域之后超出作用域,还是在类的对象超出作用域时调用类的析构函数?我想尝试模仿所显示的第二个例子的期望行为。

1 个答案:

答案 0 :(得分:0)

在我发布这个问题之后:我做了一些研究和一些试验和错误。有了这个,我选择了不同的实现和解决方案。

而不是我最初提出的建议,我最终使用了std::shared_mutexstd:shared_lock

所以在我班级的标题中,我没有保存或存储任何mutex。现在在我班级的cpp文件中。我正在使用静态全局shared_mutex

所以我的班级现在看起来像这样:

Foo.cpp

#include "Foo.h"

#include <mutex>

std::mutex g_mutex;

Foo::Foo() {
    // code not locked

    {   // scope of guard
        std::lock_guard<std::mutex> lock( g_mutex );
        // code to lock
    } // end scope destroy guard unlock mutex

    // other class code
}

Foo::someFunc() {
    // Code to lock
    std::lock_guard<std::mutex> lock( g_mutex );
}

我发现为什么它对我不起作用。在我的类的构造函数中,它从其父类或基类调用一个函数。然后父类或基类调用此类的静态成员,此类的静态成员函数也在同一个互斥锁上使用lock_guard。

我发现问题之后;我有两个选择。我可以使用2个独立的互斥锁,一个用于构造函数,一个用于静态方法。经过一番思考后我觉得如果我使用2,并且我阻塞了完整的构造函数,当前的锁和互斥锁将不会超出范围,直到类实例被销毁;然而,班级的生命几乎将是申请的整个生命周期。然后,如果我在静态方法中使用了第二个互斥锁和锁定保护程序,那么将另一个lock_guard包装在现有方法周围将是多余的。所以我得出的结论是,我需要为需要被互斥锁阻塞的代码创建一个范围块{ },以便在此部分超出范围后可以解锁,然后构造函数可以自由调用静态方法,它可以重用与现在免费相同的互斥锁。该类现在正在正常工作,它不会崩溃,也不会抛出异常。