Подтвердить что ты не робот

Когда использовать рекурсивный мьютекс?

Я понимаю, что рекурсивный мьютекс позволяет блокировать мьютекс более одного раза, не переходя в тупик, и его нужно разблокировать столько же раз. Но в каких конкретных ситуациях вам нужно использовать рекурсивный мьютекс? Я ищу ситуации на уровне кода/кода.

4b9b3361

Ответ 1

Например, если у вас есть функция, которая вызывает его рекурсивно, и вы хотите получить к нему синхронизированный доступ:

void foo() {
   ... mutex_acquire();
   ... foo();
   ... mutex_release();
}

без рекурсивного мьютекса вам сначала нужно создать функцию "точки входа", и это становится громоздким, если у вас есть набор функций, которые являются взаимно рекурсивными. Без рекурсивного мьютекса:

void foo_entry() {
   mutex_acquire(); foo(); mutex_release(); }

void foo() { ... foo(); ... }

Ответ 2

Рекурсивные и нерекурсивные мьютексы имеют различные варианты использования. Никакой тип мьютекса не может легко заменить другой. Нерекурсивные мьютексы имеют меньше накладных расходов, а рекурсивные мьютексы в некоторых ситуациях полезны или даже необходимы семантике, а в других ситуациях - опасная или даже сломанная семантика. В большинстве случаев кто-то может заменить любую стратегию, используя рекурсивные мьютексы, с другой более безопасной и более эффективной стратегией, основанной на использовании нерекурсивных мьютексов.

  • Если вы просто хотите исключить другие потоки из вашего ресурса, защищенного мьютексом, вы можете использовать любой тип mutex, но может захотеть использовать нерекурсивный мьютекс из-за его меньших накладных расходов.
  • Если вы хотите рекурсивно вызывать функции, которые блокируют один и тот же мьютекс, то они либо
    • должны использовать один рекурсивный мьютекс или
    • нужно разблокировать и заблокировать одни и те же нерекурсивные мьютексы снова и снова (остерегайтесь параллельных потоков!) (при условии, что это семантически звучит, это может быть проблема с производительностью) или
    • должны каким-то образом аннотировать, какие мьютексы они уже заблокировали (имитируя рекурсивное владение/мьютексы).
  • Если вы хотите заблокировать несколько объектов, защищенных мьютексом, из набора таких объектов, где комплекты могли быть созданы путем слияния, вы можете выбрать
    • для использования на одном объекте ровно один мьютекс, что позволяет больше потоков работать параллельно или
    • использовать для каждого объекта одну ссылку для любого , возможно, рекурсивного мьютекса, чтобы снизить вероятность отказа от блокировки всех мьютексов или
    • использовать для одного объекта одну сопоставимую ссылку для любого возможно разделяемого нерекурсивного мьютекса, обходя намерение блокировать несколько раз.
  • Если вы хотите освободить блокировку в другом потоке, кроме того, что она была заблокирована, вам нужно использовать нерекурсивные блокировки (или рекурсивные блокировки, которые явно разрешают это, а не бросают исключения).
  • Если вы хотите использовать переменные синхронизации, тогда вам нужно быть способным явно разблокировать мьютекс, ожидая любой переменной синхронизации, чтобы ресурс разрешался использовать в других потоках. Это возможно только с помощью нерекурсивных мьютексов, поскольку рекурсивные мьютексы уже могут быть заблокированы вызывающей функцией текущей функции.

Ответ 3

Если вы хотите увидеть пример кода, который использует рекурсивные мьютексы, посмотрите на источники для "Electric Fence" для Linux/Unix. "Twas - один из распространенных инструментов Unix для поиска" проверки границ "перерасхода и перезаписи чтения/записи, а также использования освобожденной памяти до Valgrind.

Просто скомпилируйте и подключите электрический забор с источниками (опция -g с gcc/g++), а затем привяжите его к вашему программному обеспечению с помощью опции ссылки - освободителю и начните переходить через вызовы в malloc/free. http://elinux.org/Electric_Fence

Ответ 4

Сегодня я столкнулся с необходимостью рекурсивного мьютекса, и, по-моему, это самый простой пример среди опубликованных ответов: Это класс, который предоставляет две функции API: Process (...) и reset().

public void Process(...)
{
  acquire_mutex(mMutex);
  // Heavy processing
  ...
  reset();
  ...
  release_mutex(mMutex);
}

public void reset()
{
  acquire_mutex(mMutex);
  // Reset
  ...
  release_mutex(mMutex);
}

Обе функции не должны запускаться одновременно, потому что они изменяют внутренности класса, поэтому я хотел использовать мьютекс. Проблема в том, что процесс() вызывает reset() внутренне, и это создаст тупик, потому что mMutex уже получен. Вместо этого блокировка с помощью рекурсивного замка устраняет проблему.

Ответ 5

Это было бы проблемой, если бы поток блокировал попытку получения (снова) уже существующего мьютекса...

Есть ли причина, по которой не допускается одновременное получение мьютекса одним и тем же потоком?