根据Threads and simple Dead lock cure和Herb Sutter中的答案,避免死锁的关键是使用锁等级。
是否有任何好的C ++库可以为此提供支持?我在Boost或Poco中找不到任何东西。
理想情况下,它是一个允许在编译时定义层次结构的系统。也许它看起来像这样:
template<class LowerLevelMutex>
class RankedMutex { ... };
class BottomMutex { ... };
typedef RankedMutex<BottomMutex> L1Mutex;
typedef RankedMutex<L1Mutex> L2Mutex;
typedef RankedMutex<L2Mutex> L3Mutex;
// ...
答案 0 :(得分:7)
是的,锁定层次结构可以有效地防止死锁;当然,你是否可以为你的程序实际定义一个层次结构(特别是在插件存在的情况下)完全是另一回事。
基本块很简单:
我希望我能做到正义的想法,请考虑草图下面的示例实现;它从未被编译/测试过。
基本互斥:
template <typename Mutex, size_t Level>
class HierarchicalMutex {
public:
friend class LevelManager;
void lock() {
LevelManager::Lock(*this);
}
void unlock() {
LevelManager::Unlock(*this);
}
private:
size_t previous;
Mutex mutex;
}; // class HierarchicalMutex
template <typename Mutex, size_t Level>
size_t level(HierarchicalMutex<Mutex,Level> const&) { return Level; }
LevelManager
的作用只是确保级别转换以正确的顺序发生。
class LevelManager {
public:
//
// Single Mutex locking
//
template <typename M>
static void Lock(M& m) {
m.previous = LevelUp(level(m));
m.mutex.lock();
}
template <typename M>
static void Unlock(M& m) {
m.mutex.unlock();
LevelDown(level(m), m.previous);
}
//
// Multiple Mutexes Group Locking
//
// Note: those should expose a "size_t level(M const&)" function,
// and calls to lock/unlock should appropriately call
// this manager to raise/lower the current level.
//
// Note: mutexes acquired as a group
// should be released with the same group.
//
template <typename M>
static void Lock(std::array_ref<M*> mutexes) { // I wish this type existed
using std::begin; using std::end;
auto begin = begin(mutexes);
auto end = end(mutexes);
end = std::remove_if(begin, end, [](M const* m) { return m == 0; });
if (begin == end) { return; }
Sort(begin, end);
size_t const previous = LevelUp(level(*std::prev(end)));
for (; begin != end; ++begin) {
begin->previous = previous;
begin->mutex.lock();
}
}
template <typename M>
static void Unlock(std::array_ref<M*> mutexes) {
using std::begin; using std::end;
auto begin = begin(mutexes);
auto end = end(mutexes);
end = std::remove_if(begin, end, [](M const* m) { return m == 0; });
if (begin == end) { return; }
Sort(begin, end);
std::reverse(begin, end);
for (auto it = begin; it != end; ++it) { it->mutex.unlock(); }
LevelDown(level(*begin), begin->previous);
}
private:
static __thread size_t CurrentLevel = 0;
template <typename It>
static void Sort(It begin, It end) {
using Ref = typename std::iterator_traits<It>::const_reference;
auto const sorter = [](Ref left, Ref right) {
return std::tie(level(left), left) < std::tie(level(right), right);
};
std::sort(begin, end, sorter);
}
static size_t LevelUp(size_t const to) {
if (CurrentLevel >= to) { throw LockHierarchyViolation(); }
CurrentLevel = to;
}
static void LevelDown(size_t const from, size_t const to) {
if (CurrentLevel != from) { throw LockHierarchyViolation(); }
CurrentLevel = to;
}
}; // class LevelManager
对于踢球,我实现了在一次射击中锁定同一级别的多重锁的可能性。
答案 1 :(得分:5)
无需单独的类来管理层次结构。 Anthony Williams(ISBN 9781933988771)可以在 C ++ Concurrency in Action 中找到一个很好的解决方案:
#include <mutex>
#include <stdexcept>
class hierarchical_mutex
{
std::mutex internal_mutex;
unsigned long const hierarchy_value;
unsigned long previous_hierarchy_value;
static thread_local unsigned long this_thread_hierarchy_value;
void check_for_hierarchy_violation()
{
if(this_thread_hierarchy_value <= hierarchy_value)
{
throw std::logic_error("mutex hierarchy violated");
}
}
void update_hierarchy_value()
{
previous_hierarchy_value=this_thread_hierarchy_value;
this_thread_hierarchy_value=hierarchy_value;
}
public:
explicit hierarchical_mutex(unsigned long value):
hierarchy_value(value),
previous_hierarchy_value(0)
{}
void lock()
{
check_for_hierarchy_violation();
internal_mutex.lock();
update_hierarchy_value();
}
void unlock()
{
this_thread_hierarchy_value=previous_hierarchy_value;
internal_mutex.unlock();
}
bool try_lock()
{
check_for_hierarchy_violation();
if(!internal_mutex.try_lock())
return false;
update_hierarchy_value();
return true;
}
};
thread_local unsigned long
hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);
int main()
{
hierarchical_mutex m1(42);
hierarchical_mutex m2(2000);
}
答案 2 :(得分:1)
在这种情况下,您可以做的主要事情就是确保您的锁始终是分层应用的(意味着嵌套)。这样你就不能在不拥有2级锁定的情况下访问3级锁定,如果没有第1级锁定就无法访问。如果没有先获得1和2,你甚至无法达到3,所以这应该可以防止出现重大问题。
您是否可以在某些出现的死锁案件中更具体?也许我们可以找到一些特别复杂的事情的解决方法,这些事情可能不像我上面描述的那样容易操作。