2016-01-23 111 views
0

我需要在一些外部函数中通过私有互斥锁来锁定对象。做这个的最好方式是什么?
我想是这样的通过私有互斥锁锁定对象的最佳方法是什么?

#include <thread> 
#include <mutex> 

class Test 
{ 
public: 
    std::lock_guard<std::mutex> lockGuard() 
     { 
      return std::lock_guard<std::mutex>(mutex); 
     } 

private: 
    std::mutex mutex; 
}; 

int main() 
{ 
    Test test; 
    std::lock_guard<std::mutex> lock = test.lockGuard(); 
    //... 
} 

lock_guard拷贝构造器中被删除。我该如何做这样的事情?

+1

使用'unique_lock'代替? –

+0

如果我用'lock_guard'替换'lock_guard',我会得到我需要的吗? – Ufx

+0

如果对象只是从外部锁定,而不是从内部锁定,为什么互斥体需要在内部声明? –

回答

1

std::unique_lock<T>定义了一个移动构造函数,可以随意使用,但这种方法本身并不是非常成功。 您应该检查您的锁定粒度,通常如果您无法提供内部同步并要求用户在对某个对象执行操作(或需要执行多个操作时)时保持锁定状态,则没有理由将该互斥锁存储在目的。

如果我不得不店内对象互斥,我会用一些包装,让我做到以下几点:

locking_wrapper<Test> test; 
test.do_locked([] (Test & instance) { 
    /* The following code is guaranteed not to interleave with 
    * any operations performed on instance from other threads. */ 
    // your code using instance here 
}); 

locking_wrapper<T>将存储存储内的对象的实例,并提供一个参考它同时保持对内部互斥锁的锁定。依靠编译器内联代码的能力,这种方法不应该超出你想要解决的问题。

上实现locking_wrapper总体思路如下:

template<typename T> 
class locking_wrapper 
{ 
    mutable std::mutex mutex; 
    // the object which requires external synchronization on access 
    T instance; 

public: 
    /* Here we define whatever constructors required to construct the 
    * locking_wrapper (e.g. value-initialize the instance, take an 
    * instance passed by user or something different) */ 
    locking_wrapper() = default; 
    locking_wrapper(const T & instance) : instance{instance} {} 

    // Takes a functor to be performed on instance while maintaining lock 
    template<typename Functor> 
    void do_locked(Functor && f) const { 
     const std::lock_guard<std::mutex> lock{mutex}; 
     f(instance); 
    } 
}; 

您可以调用任何实体传递给do_locked,你认为合适,但是是lambda表达式调用它就像我先前建议将给它最好的机会,而不用任何开销。

请注意,使用这种方法与引用,可移动对象或其他类型,我还没有预见将需要对代码进行一些修改。

+0

什么是“自我”? – erip

+0

'self'是存储在'test'里的'Test'类型的对象。也许这个名字是误导性的,我应该把它称为“实例”或“对象”。 – Hertz

+0

为什么你的括号不匹配的任何特定原因? 我会在那里问一些关于那个lambda魔法的更多解释。 –

2

改为使用std::unique_lock<std::mutex>。它是不是可复制,但它可移动的,允许你显示的模式。

#include <thread> 
#include <mutex> 

class Test 
{ 
public: 
    std::unique_lock<std::mutex> lockGuard() 
     { 
      return std::unique_lock<std::mutex>(mutex); 
     } 

private: 
    std::mutex mutex; 
}; 

int main() 
{ 
    Test test; 
    std::unique_lock<std::mutex> lock = test.lockGuard(); 
    //... 
} 

std::unique_lock<std::mutex>具有加宽API相对于std::lock_guard包括:

  • 移动构造的。
  • 移动可分配。
  • 可交换。
  • 锁()
  • 解锁()
  • try_lock()
  • try_lock_for()
  • try_lock_until()
  • 释放()
  • owns_lock()

在其它单词,因为您可以解锁并从unique_lock移出,因此不能保证对该互斥锁持有锁定(您可以检查它与owns_lock())。相比之下,lock_guard的不变量是它始终保持互斥锁。