Ответ
В C++ для этого используется рекурсивный мьютекс (std::recursive_mutex). В отличие от обычного std::mutex, который при повторной попытке захвата тем же потоком приводит к неопределённому поведению или deadlock, рекурсивный мьютекс позволяет потоку многократно захватывать одну и ту же блокировку.
Пример использования:
#include <iostream>
#include <mutex>
std::recursive_mutex rec_mutex;
int shared_data = 0;
void recursive_function(int depth) {
std::lock_guard<std::recursive_mutex> lock(rec_mutex); // Захват #1
if (depth > 0) {
// Внутри критической секции снова нужен доступ к защищённым данным
std::lock_guard<std::recursive_mutex> lock_again(rec_mutex); // Захват #2, разрешён
shared_data += depth;
recursive_function(depth - 1); // Рекурсивный вызов также захватит мьютекс
}
}
int main() {
recursive_function(3);
std::cout << "Shared data: " << shared_data << std::endl; // Вывод: Shared data: 6
return 0;
}
Важные детали:
- Каждому успешному вызову
lock()(явному или черезstd::lock_guard/std::unique_lock) должен соответствовать вызовunlock(). Рекурсивный мьютекс ведёт внутренний счётчик. - Использовать
std::recursive_mutexстоит только когда это действительно необходимо (например, при вызове общедоступных методов класса, которые сами блокируются, из другого метода этого же класса). Часто архитектуру кода можно пересмотреть, чтобы избежать рекурсивных блокировок. - Для автоматического управления временем жизни блокировки всегда предпочитайте RAII-обёртки (
std::lock_guard,std::unique_lock).