【问题标题】:Mutexes and deadlocks互斥锁和死锁
【发布时间】:2015-10-26 21:12:14
【问题描述】:

在询问有关互斥锁here 的问题后,我收到了有关死锁的警告。

我在下面整理的示例是否是避免死锁的合理方法?

class Foo
{
public:
    Foo();

    void Thread();

    int GetWidgetProperty();
    int GetGadgetProperty();

private:

    Widget widget_;
    Gadget gadget_;

    VDK::MutexID widgetLock;
    VDK::MutexID gadgetLock;
};


Foo::Foo()
    : widget_(42)
    , gadget_(widget_)
{
    widgetLock = VDK::CreateMutex();
    gadgetLock = VDK::CreateMutex();
}

void Foo::Thread()
{
    while(1)
    {
        VDK::AcquireMutex(widgetLock);
        // Use widget
        VDK::ReleaseMutex(widgetLock);

        VDK::AcquireMutex(widgetLock);
        VDK::AcquireMutex(gadgetLock);
        // use gadget
        VDK::ReleaseMutex(widgetLock);
        VDK::ReleaseMutex(gadgetLock);
    }
}

int Foo::GetWidgetProperty()
{
    VDK::AcquireMutex(widgetLock);
    return widget_.GetProp();
    VDK::ReleaseMutex(widgetLock);
}

int Foo::GetGadgetProperty()
{
    VDK::AcquireMutex(widgetLock);
    VDK::AcquireMutex(gadgetLock);
    return gadget.GetProp();
    VDK::ReleaseMutex(widgetLock);
    VDK::ReleaseMutex(gadgetLock);  
}

由于调用 GetGadgetProperty 可能会导致使用小部件,我猜我们还需要在这里用锁保护自己。我的问题是,我是否以正确的顺序要求和释放它们?

【问题讨论】:

  • 努力始终以相同的顺序锁定。这个SO post 非常详细,正是您要找的。​​span>
  • 我不明白你的代码。您是否正在尝试实现自己的并发原语?你有什么理由不使用 C++11 的线程支持库吗?
  • 我正在为不支持 C++11 的嵌入式系统进行开发。 VDK 是 Analog Devices 的内核。
  • 我在嵌入式编程中的方法是尽可能避免这样的锁定事件。我更喜欢演员的方法。您的活动 C++ 对象(其中包含一个线程)接收消息并对其进行操作,然后根据需要发送消息。这样,您通常需要正确锁定的唯一锁定与消息队列有关。

标签: c++ mutex deadlock


【解决方案1】:

您的代码有明显的死锁。您不能在 return 语句后释放互斥锁。更重要的是,最好以相反的(锁定)顺序解锁它们。正确的代码应该是这样的:

int Foo::GetWidgetProperty()
{
    VDK::AcquireMutex(widgetLock);
    int ret = widget_.GetProp();
    VDK::ReleaseMutex(widgetLock);
    return ret;
}

int Foo::GetGadgetProperty()
{
    VDK::AcquireMutex(widgetLock);
    VDK::AcquireMutex(gadgetLock);
    int ret = gadget.GetProp();
    VDK::ReleaseMutex(gadgetLock);  
    VDK::ReleaseMutex(widgetLock);
    return ret;
}

【讨论】:

  • Ups,好尴尬!
【解决方案2】:

更好的方法是依靠 RAII 为您完成这项工作。

我邀请您阅读有关std::lock_guard 的信息。基本原则是通过声明一个对象来获取互斥锁。并且互斥体在其生命周期结束时自动释放。

现在,您可以将块作用域用于需要以这种方式锁定互斥锁的代码区域:

{
    std::lock_guard lockWidget{widgetMutex};//Acquire widget mutex
    std::lock_guard lockGadget{gadgetMutex};//Acquire gadget mutex
    //do stuff with widget/gadget
    //...
    //As the lock_guards go out of scope in the reverse order of 
    //declaration, the mutexes are released
}

当然,这适用于标准库互斥锁,因此您必须适应您的使用。

这将防止错误,例如在 return 语句之后尝试释放互斥锁,这显然永远不会发生,或者面对在您实际释放互斥锁之前可能发生的异常。

【讨论】:

  • std::lock_guard 很有用。但他要求最佳实践以避免死锁。
  • @LeFlou 好吧,它们是最佳实践的一部分。它们特别避免了在面对异常时可能出现的死锁。此外,它避免了人为错误,因为您不必将每个锁都与解锁配对。
  • 如果不提及 RAII,就不可能谈论最佳实践,它实际上解决了问题和其他可能出现的问题。 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多