互斥锁和死锁
Mutexes and deadlocks
在询问有关互斥锁的问题后 ,我收到了有关死锁的警告。
我在下面放在一起的示例是否是避免死锁的合理方法?
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 可能导致使用小部件,我猜我们还需要在此处使用锁来保护我们自己。我的问题是,我是否以正确的顺序要求和释放它们?
你的代码有明显的死锁。您不能在 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;
}
更好的方法是依靠 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 语句之后尝试释放互斥量,这显然永远不会发生,或者在您实际释放互斥量之前发生异常。
在询问有关互斥锁的问题后
我在下面放在一起的示例是否是避免死锁的合理方法?
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 可能导致使用小部件,我猜我们还需要在此处使用锁来保护我们自己。我的问题是,我是否以正确的顺序要求和释放它们?
你的代码有明显的死锁。您不能在 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;
}
更好的方法是依靠 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 语句之后尝试释放互斥量,这显然永远不会发生,或者在您实际释放互斥量之前发生异常。