我有一个类,其中包含一个互斥锁和一个对象。每次需要访问该包含的对象时,都会调用一个方法来锁定互斥锁并返回包含的对象。下面是代码:
这段代码的思路是"包装"一个对象并保护它免受来自多个线程的多次访问;被包装的对象具有私有可见性,唯一访问它的方式是通过内部类"Handler",预期的用法如下:
template <typename MUTEX, typename RESOURCE>
class LockedResource
{
using mutex_t = MUTEX;
using resource_t = RESOURCE;
mutex_t m_mutex;
resource_t m_resource;
public:
template <typename ... ARGS>
LockedResource(ARGS &&... args) :
m_resource(std::forward<ARGS>(args) ...)
{}
class Handler
{
std::unique_lock<mutex_t> m_lock; // unique lock
resource_t &m_resource; // Ref to resource
friend class LockedResource;
Handler(mutex_t &a_mutex, resource_t &a_resource) :
m_lock(a_mutex), // mutex automatically locked
m_resource(a_resource)
{ std::cout << "Resource locked\n"; }
public:
Handler(Handler &&a_handler) :
m_lock(std::move(a_handler.m_lock)),
m_resource(a_handler.m_resource)
{ std::cout << "Moved\n"; }
~Handler() // mutex automatically unlocked
{ std::cout << "Resource unlocked\n"; }
RESOURCE *operator->()
{ return &m_resource; }
};
Handler get()
{ return {m_mutex, m_resource}; }
};
template <typename T> using Resource = LockedResource<std::mutex, T>;
这段代码的思路是"包装"一个对象并保护它免受来自多个线程的多次访问;被包装的对象具有私有可见性,唯一访问它的方式是通过内部类"Handler",预期的用法如下:
LockedResource<std::mutex, Foo> locked_foo;
void f()
{
auto handler = locked_foo.get(); // this will lock the locked_foo.m_mutex;
handler->some_foo_method();
// going out of the scope will call the handler dtor and
// unlock the locked_foo.m_mutex;
}
所以,如果我没有错的话,调用LockedResource::get
方法会创建一个LockedResource::Handle
值,该值会在整个Handle
的生命周期中锁定LockedResource::m_mutex
...但我一定是错误的,因为下面的代码不会导致死锁:
LockedResource<std::mutex, std::vector<int>> locked_vector{10, 10};
int main()
{
/*1*/ auto vec = locked_vector.get(); // vec = Resource<vector>::Handler
/*2*/ std::cout << locked_vector.get()->size() << '\n';
/*3*/ std::cout << vec->size() << '\n';
return 0;
}
我原本期望代码中的/*1*/
行会锁定locked_vector.m_mutex
,而/*2*/
行会尝试锁定已经被锁定的同一互斥量导致死锁,但实际输出结果如下:
Resource locked
Resource locked
10
Resource unlocked
10
Resource unlocked
- 第二个
::get()
不应该导致死锁吗? - 我是通过同一个锁来访问包装的资源还是我理解错了什么?
这里是示例代码。
C++11
标准,那么你可能会遇到死锁(或者抛出异常,或者触发assert
)。然而,如果std::mutex
是一个递归互斥锁的包装器(这在嵌入式系统中很常见),那么你将看到上述输出。 - MehrwolfGCC 5.1
时,行为是不同的。例如,请参见此处。 - Mehrwolf