我需要在某些外部函数中通过私有互斥锁来锁定对象。最好的方法是什么?
我想要这样的东西
#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
复制构造函数。我怎么能这样做?
答案 0 :(得分: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>
相对于std::lock_guard
具有更广泛的API,包括:
换句话说,由于您可以从unique_lock
解锁并移动,因此无法保证锁定在互斥锁上(您可以检查它是否与owns_lock()
一起)。相比之下,lock_guard
的不变量是它始终锁定互斥锁。
答案 1 :(得分: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-expression调用它将为其提供内联的最佳机会,而不会产生任何开销。
请注意,使用此方法与引用,可移动对象或我尚未预见的其他类型将需要对代码进行一些修改。