Для C++17 запланирован класс shared_mutex. И shared_timed_mutex уже в C++14. (Кто знает, почему они пришли именно в таком порядке, но все равно.) Затем идет recursive_mutex a> и recursive_timed_mutex, начиная с C++11. Что мне нужно, так это shared_recursive_mutex. Я что-то пропустил в стандарте или мне нужно ждать стандартизированной версии еще три года?
Если в настоящее время такой возможности нет, что может быть простой (первый приоритет) и эффективной (второй приоритет) реализацией такой функции с использованием только стандартного C++?
Я бы не стал писать такое. как насчет того, чтобы не делать блокировку рекурсивной? David Haim 14.04.2016
Необходимость рекурсивного мьютекса обычно является признаком того, что код необходимо переработать. Pete Becker 14.04.2016
Рекурсивное свойство мьютекса работает с термином владелец, который в случае shared_mutex определен нечетко: несколько потоков могут вызывать .lock_shared() в в то же время.
Предполагая, что владелец является потоком, который вызывает .lock() (не .lock_shared()!), реализация рекурсивного общего мьютекса может быть просто получена из shared_mutex:
Поле .owner нужно объявить атомарным, так как в методе .lock() оно проверяется без защиты от параллельного доступа.
Если вы хотите рекурсивно вызывать метод .lock_shared(), вам необходимо поддерживать карту владельцев, а доступ к этой карте должен быть защищен дополнительным мьютексом.
Разрешение потоку с активным .lock() вызывать .lock_shared() усложняет реализацию.
Наконец, разрешение потоку перемещать блокировку от .lock_shared() к .lock() является нет-нет, так как это приводит к возможной взаимоблокировке, когда два потока пытаются выполнить это продвижение.
Опять же, семантика рекурсивногообщего мьютекса будет очень хрупкой, поэтому лучше вообще его не использовать.
Ответ 2 · рейтинг 7 · Maxim Egorushkin
Если вы работаете на платформе Linux/POSIX, вам повезло, потому что мьютексы C++ созданы по образцу мьютексов POSIX. POSIX предоставляет больше возможностей, в том числе рекурсивность, совместное использование процессов и многое другое. Оборачивать примитивы POSIX в классы C++ несложно.
try_lock и try_lock_shared оставили в качестве упражнения.
И блокировка, и разблокировка совместно блокируют мьютекс дважды (это безопасно, так как ветки действительно о том, "контролирует ли этот поток мьютекс", и другой поток не может изменить этот ответ с "нет" на "да" или наоборот) . Вы могли бы сделать это с одной блокировкой с ->* вместо ->, что сделало бы это быстрее (за счет некоторой сложности в логике).
Вышеупомянутое не поддерживает монопольную блокировку, а затем совместную блокировку. Это сложно. Он не может поддерживать общую блокировку, а затем обновление до уникальной блокировки, потому что в принципе невозможно предотвратить взаимоблокировку, когда 2 потока пытаются это сделать.
Эта последняя проблема может быть причиной того, почему рекурсивные общие мьютексы - плохая идея.
Ответ 4 · рейтинг 3 · Useless
Возможно создать общий рекурсивный мьютекс, используя существующие примитивы. Однако я не рекомендую этого делать.
Это не просто, и обертка существующей реализации POSIX (или любой другой, родной для вашей платформы), скорее всего, будет более эффективной.
Если вы решите написать собственную реализацию, ее эффективность по-прежнему будет зависеть от особенностей конкретной платформы, поэтому вы либо пишете интерфейс с разными реализациями для каждой платформы, либо выбираете платформу и могли бы так же легко использовать собственные средства (POSIX или что-то еще).
Я, конечно, не собираюсь приводить пример реализации рекурсивной блокировки чтения/записи, потому что это совершенно неразумный объем работы для ответа на вопрос о переполнении стека.
Если поток владеет общей блокировкой, он также может получить эксклюзивную блокировку, не отказываясь от своей общей блокировки. (Это, конечно, требует, чтобы ни один другой поток в настоящее время не имел общей или монопольной блокировки)
И наоборот, поток, владеющий эксклюзивной блокировкой, может получить общую блокировку.
Интересно, что эти свойства также позволяют обновлять/откатывать замки.