нерекурсивный мьютекс
Большинство мьютексов являются (или, по крайней мере, должны быть) нерекурсивными.Мьютекс - это объект, который может быть получен или выпущен атомарно, что позволяет защитить данные, которые совместно используются несколькими потоками, от состояния гонки, повреждения данных и других неприятных вещей.
Один мьютекс должен быть получен только один раз одним потоком в одной цепочке вызовов .Попытка получить (или удержать) один и тот же мьютекс дважды в одном и том же контексте потока должна рассматриваться как недопустимый сценарий и должна обрабатываться надлежащим образом (обычно через ASSERT, поскольку вы нарушаете фундаментальный контракт вашего кода).
Рекурсивный мьютекс
Это следует считать запахом кода или взломом.Единственный способ, которым рекурсивный мьютекс отличается от стандартного мьютекса, состоит в том, что рекурсивный мьютекс может быть получен несколько раз одним и тем же потоком.
Основной причиной необходимости рекурсивного мьютекса является отсутствие владельца и отсутствиечеткая цель или разграничение между классами.Например, ваш код может вызывать другой класс, который затем вызывает ваш класс.Затем начальный класс может попытаться снова получить тот же мьютекс, и, поскольку вы хотите избежать сбоя, вы реализуете его как рекурсивный мьютекс.
Этот вид иерархии классов с переворотом может привести ко всем видамголовные боли, а рекурсивный мьютекс предоставляет только решение для помощи при перевязке для более фундаментальной архитектурной проблемы.
Независимо от типа мьютекса, оно всегда должно быть поток, который получает и освобождает тот же мьютекс.Общий шаблон, который вы используете в коде, выглядит примерно так:
Thread 1
Acquire mutex A
// Modify or read shared data
Release mutex A
Thread 2
Attempt to acquire mutex A
Block as thread 1 has mutex A
When thread 1 has released mutex A, acquire it
// Modify or read shared data
Release mutex A
Он становится более сложным, когда у вас есть несколько мьютексов, которые могут быть получены одновременно (скажем, мьютекс A и B).Существует риск того, что вы попадете в тупиковую ситуацию, подобную этой:
Thread 1
Acquire mutex A
// Access some data...
*** Context switch to thread 2 ***
Thread 2
Acquire mutex B
// Access some data
*** Context switch to thread 1 ***
Attempt to acquire mutex B
Wait for thread 2 to release mutex B
*** Context switch to thread 2 ***
Attempt to acquire mutex A
Wait for thread 1 to release mutex A
*** DEADLOCK ***
Теперь у нас есть ситуация, когда каждый поток ожидает, пока другой поток освободит блокировку other - это называется шаблоном взаимоблокировки ABBA .
. Чтобы предотвратить эту ситуацию, важно, чтобы каждый поток всегда получал мьютексы в порядке (например,всегда A, затем B).