互斥锁和死锁

3
在关于互斥锁的问题上这里,有人警告我会出现死锁。
下面我提出的示例会是避免死锁的合理方式吗?
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帖子详细说明了这一点,正是你所需要的。 - Richard Dally
我不理解你的代码。你是在尝试实现自己的并发原语吗?你为什么不使用C++11的线程支持库呢? - 5gon12eder
1
我正在为不支持C++11的嵌入式系统进行开发。VDK是来自Analog Devices的内核。 - Q-bertsuit
2
我的嵌入式编程方法是尽可能避免像这样的锁定竞争。我更喜欢使用Actor方法。你的活动C++对象(内部有一个线程)接收消息并对其进行操作,然后根据需要发送消息。这样,你通常只需要正确处理与消息队列相关的锁定即可。 - BitTickler
2个回答

7

您的代码明显存在死锁问题。在返回语句后,您不能释放互斥锁。而且最好按与加锁相反的顺序解锁它们。正确的代码应该像这样:

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;
}

哎呀,真尴尬! - Q-bertsuit

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
}

当然,这适用于标准库的互斥锁,因此您需要根据自己的使用进行调整。
这将防止出现错误,例如在返回语句之后尝试释放互斥锁,显然永远不会发生,或者在实际释放互斥锁之前发生异常。

1
std::lock_guard 很有用。但是他正在寻求最佳实践以避免死锁。 - Richard Dally
@LeFlou 嗯,它们是该领域的最佳实践的一部分。它们显著地避免了在异常情况下可能出现的死锁。此外,它还避免人为错误,因为您无需将每个锁与解锁配对。 - JBL
2
你谈论最佳实践时不可能不提RAII,它可以解决问题,并且还能应对其他可能出现的问题。+1 - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接