Прежде всего, я предполагаю, что вопрос заключается в том, чтобы избежать взаимоблокировки при блокировке нескольких произвольных мьютексов. Важно всегда использовать одно и то же соглашение об упорядочении во всем коде, используя набор мьютексов. Если вы можете гарантировать, что мьютекс A всегда будет блокироваться раньше B, B всегда будет блокироваться раньше C, а A всегда раньше C, вы избежите взаимоблокировки.
В первом примере кода принято сначала блокировать мьютекс с меньшим адресом памяти. Это будет работать нормально, поскольку порядок адресов является инвариантным. Вторая версия — это официальный Boost. способ избежать взаимоблокировки. В документации не указано, какой порядок выполняется внутри. Я не рекомендую искать его в исходниках и использовать эту информацию где-либо еще в вашем коде — это может привести к незначительным поломкам, если библиотека изменится.
Если вы начинаете с нуля (ранее вы не удерживали в своем коде более одного мьютекса за раз), использование метода Boost определенно предпочтительнее — вам не нужно беспокоиться о точном порядке, пока вы его оставляете. до повышения каждый раз. Если остальная часть вашего кода использует порядок памяти, вы должны использовать его. Если в вашем коде используется совсем другое соглашение, вам нужно применить его и здесь. Ни при каких обстоятельствах вы не должны смешивать соглашения в любом наборе замков, которые могут удерживаться одновременно, это просто напрашивается на неприятности.
Чтобы ответить на вопрос в комментарии:
Пользовательская схема упорядочения блокировок может быть полезна в определенных обстоятельствах, особенно если вам нужно удерживать некоторые блокировки (A) в течение длительного времени, а некоторые (B) — только кратковременно, в то время как удерживать долгую. Например, если вам нужно выполнять длительные задания над объектами типа A, которые кратковременно влияют на множество экземпляров B. По соглашению всегда сначала нужно получить блокировку для A, а затем затем блокировку для B объект:
void doStuff(A& a, std::list<B*> bs)
{
boost::unique_lock<boost::mutex> la(a.mutex); // lock a throughout
for (std::list<B*>::iterator ib = bs.begin(); ib != bs.end(); ++ib)
{
// lock each B only for one loop iteration
boost::unique_lock<boost::mutex> lb(ib->mutex);
// work on a and *ib
// ...
}
}
Вы можете отказаться от блокировки A между каждой итерацией цикла и использовать порядок блокировки Boost/C++0x, но в зависимости от того, что делает doStuff(), это может сделать алгоритм более сложным или запутанным. .
Другой пример: в средах выполнения, где объекты не обязательно остаются в одном и том же месте памяти (например, из-за копирования сборки мусора), использование адреса памяти для упорядочения не будет надежным. Таким образом, вы можете присвоить каждому объекту уникальный идентификатор и основывать порядок блокировок на порядке идентификаторов.
person
pmdj
schedule
25.01.2011
true
илиfalse
для обоих сравнений. Правильный способ — использовать специализациюstd::less
(и kin) либо дляvoid*
, либо дляboost::mutex*
, поскольку функторы функционального сравнения обеспечивают полное упорядочение всех специализаций указателей. (Вам по-прежнему не гарантируется результат в частности, но гарантируется действительный порядок.) - person GManNickG   schedule 25.01.2011