Существует широко известный способ блокировки нескольких замков, которые полагаются на выбор фиксированных линейных заказов и блокировки доступа в соответствии с этим заказом.
Это было предложено, например, в ответе "Приобрести замок на двух мьютексах и избежать тупика" . В частности, решение, основанное на сравнении адресов, кажется довольно элегантным и очевидным.
Когда я попытался проверить, как это реализовано, я, к моему удивлению, нашел, что это решение широко не используется.
Чтобы процитировать Kernel Docs - ненадежное руководство по блокировке:
Учебники расскажут вам, что если вы всегда блокируете в том же порядке, вы никогда не будет такого тупика. Практика скажет вам, что это подход не масштабируется: когда я создаю новый замок, я не понимаю достаточно ядра, чтобы выяснить, где в иерархии блокировки 5000 это будет соответствовать.
PThreads, похоже, не имеет такого механизма.
Boost.Thread придумал
совершенно другое решение, lock()
для нескольких (от 2 до 5) мьютексов основано на попытке и блокировке как многих мьютексов, так и на момент.
Это фрагмент исходного кода Boost.Thread(Boost 1.48.0, boost/thread/locks.hpp: 1291):
template<typename MutexType1,typename MutexType2,typename MutexType3>
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
по успеху и количеству мьютексов, которые в противном случае не были успешно заблокированы.
Почему это решение лучше, чем сравнение адресов или других типов идентификаторов? Я не вижу никаких проблем с сопоставлением указателей, чего можно избежать, используя эту "слепую" блокировку.
Есть ли другие идеи о том, как решить эту проблему на уровне библиотеки?