确保,功能在lock_guard下运行

时间:2017-01-30 23:06:46

标签: c++ c++11 mutex

我有一个函数foo(),它可以在繁重的多线程环境中从不同的地方调用。

它正在修改一些敏感数据,但由于复杂性,我无法使用内部此函数std::lock_guard... - 这必须由调用函数确保。原因是该函数连续多次被调用,并且修改后的数据[内部foo()]必须同时被其他线程触及,直到一个线程完成所有修改几次调用foo()

我们都是人 - 尤其是我 - 我正在考虑如何确保foo()函数确实在lock_guard...控件之下并且我并没有忘记锁定范围。

对我来说 - 在gcc下运行并且我不必考虑任何独立于平台的解决方案 - 我发现了这种方式:

auto foo( __attribute__ ((unused)) std::lock_guard<std::mutex> & guardActive ) -> void
{
   ...
}

然后,必须像这样调用foo(),否则gcc不会编译它:

{
    std::lock_guard<std::mutex> lockScope( mutexObject );
    // block of commands calling foo() which must NOT be interrupted
    ...
    foo( lockScope );
    ...
    foo( lockScope );
    ...
    foo( lockScope );
    ...
}

我的问题:

这样可行,还是有更好的解决方案?不能使用可重入的互斥锁。

3 个答案:

答案 0 :(得分:0)

我确信,我开始的方式是正确的。我发现这篇帖子On unnamed parameters to functions, C++ 或多或少证实了我。

现在,我删除了参数名称,它实现如下:

auto foo( std::lock_guard<std::mutex> & ) -> void
{
   ...
}

我发现它更优雅,因为它不使用gcc扩展,并且它是完全C ++兼容的。

答案 1 :(得分:0)

最全面的方法是创建“交易对象”。它是“委托模式”的变体,其中事务对象协调对对象的访问。它可以被认为是速度lock_guard<>。与lock_guard<>类似,它使用RAII锁定然后解锁对象,允许在其间进行多个操作。 与lock_guard<>优化一样,优化可能只需解开所有内联调用,并使代码与直接管理mutex完全一致。

此示例使用private禁止访问事务外的“需要锁定”成员。

在许多情况下,这肯定会过度工程,特别是考虑到完整实施需要FooConstTransFooTrans类,具体取决于引用对象的const限定。

#include <iostream>
#include <mutex>
#include <type_traits>

class FooTrans;

class Foo {
public:
    Foo():m_x(0)
    {}

friend class FooTrans;    
private:
    int get_x() const{
        return m_x;
    }

    void set_x(int x){
        m_x=x;
    }

    void lock() const{
        m_mutex.lock();
    }

    void unlock() const{
        m_mutex.unlock();
    }

private:
    mutable std::mutex m_mutex;
    int m_x;
};

class FooTrans final {
public: 
    FooTrans(Foo& foo):m_foo(foo){
        m_foo.lock();
    }

    void set_x(int x){
        m_foo.set_x(x);
    }

    int get_x(){
        return m_foo.get_x();
    }

    ~FooTrans(){
        m_foo.unlock();
    }


    private:
      Foo& m_foo;
};

int main() {

    Foo foo;
    FooTrans ft(foo);

    ft.set_x(3);
    //ft is guaranteeing the next line outputs '3' even if other threads are trying to access foo.
    std::cout << ft.get_x() <<std::endl;

    return 0;
}

答案 2 :(得分:-1)

如果foo应该总是使用相同的互斥锁,例如,如果它是一个成员函数并且该类包含一个互斥锁,我会使用这种方法:

void foo() {
    std::lock_guard<std::mutex> lock(someMutex);
    ...
}

否则,它经常被推送到方法/类的调用者,以使用如下锁定来调用来自外部不同线程的调用函数:

{
    std::lock_guard<std::mutex> lock(someMutex);
    foo();
}

如果你真的认为这容易出错(我不这样做)你可以将它包装在lambda中以方便:

auto lockedFoo = [&]{ std::lock_guard<std::mutex> lock(someMutex); foo(); };