Несколько стратегий блокировки мьютексов и почему библиотеки не используют сравнение адресов

Существует широко известный способ блокировки нескольких блокировок, основанный на выборе фиксированного линейного порядка и получении блокировок в соответствии с этим порядком.

Это было предложено, например, в ответе на «Получить блокировку двух мьютексов и избежать взаимоблокировки» . В частности, решение, основанное на сравнении адресов, кажется довольно элегантным и очевидным .

Когда я попытался проверить, как это на самом деле реализовано, я, к своему удивлению, обнаружил, что это решение не получило широкого распространения.

Чтобы процитировать Kernel Docs -Ненадежное руководство по блокировке:

Textbooks will tell you that if you always lock in the same order, you will never get this kind of deadlock. Practice will tell you that this approach doesn't scale: when I create a new lock, I don't understand enough of the kernel to figure out where in the 5000 lock hierarchy it will fit.

Похоже, что PThreads вообще не имеет встроенного такого механизма.

Boost.Thread придумал совершенно другое решение,lock()для нескольких (от 2 до 5 )мьютексов основан на попытке и блокировке как можно большего количества мьютексов, которое возможно в данный момент.

Это фрагмент исходного кода Boost.Thread (Boost 1.48.0, boost/thread/locks.hpp :1291):

template
void lock(MutexType1& m1,MutexType2& m2,MutexType3& m3)
{    
    unsigned const lock_count=3;
    unsigned lock_first=0;
    for(;;)
    {    
        switch(lock_first)
        {    
        case 0:
            lock_first=detail::lock_helper(m1,m2,m3);
            if(!lock_first)
                return;
            break;
        case 1:
            lock_first=detail::lock_helper(m2,m3,m1);
            if(!lock_first)
                return;
            lock_first=(lock_first+1)%lock_count;
            break;
        case 2:
            lock_first=detail::lock_helper(m3,m1,m2);
            if(!lock_first)
                return;
            lock_first=(lock_first+2)%lock_count;
            break;
        }    
    }    
}    

где lock_helperвозвращает 0в случае успеха и количество мьютексов, которые не были успешно заблокированы в противном случае.

Почему это решение лучше ,чем сравнивать адреса или любые другие идентификаторы? Я не вижу никаких проблем со сравнением указателей, которых можно избежать с помощью такого рода «слепой» блокировки.

Есть ли другие идеи, как решить эту проблему на уровне библиотеки?

20
задан Community 23 May 2017 в 12:18
поделиться