应该'这段代码不会导致死锁吗
Shouldn't this code lead to a deadlock?
我有一个类,它包含一个互斥和一个对象,每次我需要访问包含的对象时,都会调用一个方法来锁定互斥并返回包含的对象,让我们看看代码:
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 lockedn"; }
public:
Handler(Handler &&a_handler) :
m_lock(std::move(a_handler.m_lock)),
m_resource(a_handler.m_resource)
{ std::cout << "Movedn"; }
~Handler() // mutex automatically unlocked
{ std::cout << "Resource unlockedn"; }
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()
不应该导致死锁吗 - 我正在通过同一个锁访问封装的资源,或者我误解了什么
下面是示例代码。
好吧,快速测试显示以下内容:
- GCC-显示问题中显示的输出
- Clang进程扼杀了我使用过的在线编译器。所以陷入僵局
- MSVC2013-"设备或资源繁忙:设备或资源忙碌"-已引发。它检测到有人试图锁定同一线程上已锁定的互斥对象
对此有什么标准?
30.4.1.2.1/4[注意:如果拥有互斥对象的线程对该对象调用lock((,则程序可能死锁可以检测到死锁,则可能会观察到resource_deadlock_would_ecurse错误条件尾注]
但根据30.4.1.2/13,它应该抛出其中一个:
— resource_deadlock_would_occur — if the implementation detects that a deadlock would occur.
— device_or_resource_busy — if the mutex is already locked and blocking is not possible.
所以答案是肯定的,你观察到的是不正确的行为。它应该要么盖帽,要么投球,但不要继续,因为什么都没有发生
观察到的行为是可能的,因为代码中有UB。根据17.6.4.11,违反Requires条款为UB,在30.4.1.2/7中,我们有以下要求:
需要:如果m的类型为std::mutex、std::timed_mutex或std::shared_timed_mutex,调用线程不拥有该互斥。
感谢@T.C.指出UB。
我不熟悉这个特定于的互斥体/资源实现,但这种同步原语通常包含LOCK COUNT,并允许同一线程多次锁定同一对象。
当互斥锁被解锁的次数与被锁定的次数相同时,另一个线程可以自由锁定它。
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 用C++中的std::condition_variable将线程置于死锁中会有风险吗
- 使用 std::async 时死锁,将来作为成员
- 如何调试读写器锁的死锁?
- 为什么在Visual Studio 2013上的std::this_thread::sleep_for上死锁
- localtime() 函数正在调用 ___lll_lock_wait_private(),这会使线程陷入死锁
- 如何重现 Boost 进程文档提示的死锁?
- 多线程Windows GUI应用程序中的死锁
- 为什么printf会导致与future.get的死锁,而cout则不会?
- C++中具有阻塞队列和障碍的死锁
- 死锁使用 std::mutex 来保护多个线程中的 cout
- 避免并发等待对象中的死锁
- 在VC++中从DLLMAIN内部调用D3D的CREATEDEVICE时,它会创建一个死锁(loaderlock?)。有没有办法克服这个问题?最终目标内
- 当用2个螺纹锁定时,将recursive_mutex死锁
- 我的代码中存在死锁/访问冲突,即使我已经相互排除了它
- C++代码中的死锁
- c++ Boost线程.如何使用递归尝试锁?死锁发生在并发代码中
- 应该'这段代码不会导致死锁吗
- 用于防止代码中死锁的锁定策略和技术