这个代码不应该导致死锁吗? [英] Shouldn't this code lead to a deadlock?

查看:351
本文介绍了这个代码不应该导致死锁吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类包含一个互斥和一个对象,每次我需要访问包含的对象,调用一个方法来锁定互斥,并返回te包含的对象,让我们看看代码:

I have a class which contains a mutex and an object, each time I need to access the contained object, a method is called to lock the mutex and return te contained object, let's see the code:

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 ,预期的用法如下:

The idea behind this code is to wrap an object and protect it from multiple access from multiple threads; the wrapped object have private visibility and the only way to access it is through the internal class Handler, the expected usage is the following:

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 值,该值锁定 LockedResource :: m_mutex Handle ...的整个生命周期,但我必须被误认为是因为下面的代码不会导致死锁:

So, if I'm not mistaken, calling the LockedResource::get method creates a LockedResource::Handle value which locks the LockedResource::m_mutex for the entire lifetime of the Handle... but I must be mistaken because the code below doesn't cause a deadlock:

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 * / 锁定同一已锁定的互斥引起死锁,但输出如下:

I was expecting the line /*1*/ to lock the locked_vector.m_mutex and then the line /*2*/ try to lock the same already locked mutex causing deadlock, but the output is the following:


Resource locked
Resource locked
10
Resource unlocked
10
Resource unlocked





  • 应该不是第二个 :: get()导致死锁? / li>
  • 我通过相同的锁访问包装的资源或者我误解了某些东西?

    • Shouldn't the second ::get() lead to a deadlock?
    • I'm accessing the wrapped resource through the same lock or I am misunderstanding something?
    • 这是 示例代码

      推荐答案

      好,快速测试显示以下内容:

      Well, quick tests show the following:


      1. GCC -

      2. Clang - 在我使用的在线编译器上杀死了进程。

      3. MSVC2013 - 设备或资源繁忙:设备或资源繁忙 - 被抛出。它检测到在同一个线程上锁定已经锁定的互斥锁的尝试。

      有什么标准?

      What standard has to say about it?


      30.4.1.2.1 / 4 [注意:如果拥有一个互斥对象的线程调用锁,程序可能 )。如果实现
      可以检测到死锁,则可以观察到resource_deadlock_would_occur错误条件。 - end note]

      30.4.1.2.1/4 [ Note: A program may deadlock if the thread that owns a mutex object calls lock() on that object. If the implementation can detect the deadlock, a resource_deadlock_would_occur error condition may be observed. — end note ]

      但根据30.4.1.2/13,它应该抛出以下之一:

      But according to 30.4.1.2/13 it should throw one of these:

      — 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中我们有以下要求:

      The behavior observed is possible since you have UB in the code. According to 17.6.4.11, violation of a Requires clause is UB and in 30.4.1.2/7 we have the following requirement:


      需要:如果m的类型为std :: mutex,std :: timed_mutex或
      std :: shared_timed_mutex,则调用线程不拥有互斥体。 p>

      Requires: If m is of type std::mutex, std::timed_mutex, or std::shared_timed_mutex, the calling thread does not own the mutex.

      感谢@TC用于指出UB。

      Thanks to @T.C. for pointing out about UB.

      这篇关于这个代码不应该导致死锁吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆