В C ++ 14, похоже, пропущен механизм проверки, std::mutex
заблокирован ли объект или нет. Посмотрите этот ТАК вопрос:
/programming/21892934/how-to-assert-if-a-stdmutex-is-locked
Есть несколько способов обойти это, например, используя;
std::mutex::try_lock()
std::unique_lock::owns_lock()
Но ни один из них не является особенно удовлетворительным решением.
try_lock()
разрешено возвращать ложный минус и имеет неопределенное поведение, если текущий поток заблокировал мьютекс. У этого также есть побочные эффекты. owns_lock()
требует построения unique_lock
поверх оригинала std::mutex
.
Очевидно, я мог бы бросить свой собственный, но я бы лучше понял мотивы для текущего интерфейса.
Возможность проверить состояние мьютекса (например std::mutex::is_locked()
) не кажется мне эзотерическим запросом, поэтому я подозреваю, что Комитет по стандартизации намеренно пропустил эту функцию, а не упустил ее.
Зачем?
Изменить: Хорошо, возможно, этот вариант использования не так часто, как я ожидал, поэтому я проиллюстрирую мой конкретный сценарий. У меня есть алгоритм машинного обучения, который распределен по нескольким потокам. Каждый поток работает асинхронно и возвращается в основной пул после завершения задачи оптимизации.
Затем он блокирует мастер мьютекс. Затем поток должен выбрать нового родителя, из которого нужно изменить мутацию потомка, но может выбрать только тех родителей, у которых в настоящее время нет потомков, которые оптимизируются другими потоками. Поэтому мне нужно выполнить поиск, чтобы найти родителей, которые в данный момент не заблокированы другим потоком. Нет риска изменения статуса мьютекса во время поиска, так как мьютекс основного потока заблокирован. Очевидно, что есть и другие решения (в настоящее время я использую логический флаг), но я подумал, что мьютекс предлагает логическое решение этой проблемы, поскольку он существует для синхронизации между потоками.
is_locked
?Ответы:
Я вижу как минимум две серьезные проблемы с предложенной операцией.
Первый из них уже упоминался в комментарии @ gnasher729 :
Единственный способ убедиться, что свойство мьютекса «в данный момент заблокировано» не изменилось, это заблокировать его самостоятельно.
Вторая проблема, которую я вижу, состоит в том, что если вы не заблокируете мьютекс, ваш поток не синхронизируется с потоком, который ранее заблокировал мьютекс. Поэтому говорить о «до» и «после», а также о том, заблокирован ли мьютекс или нет, не совсем ясно, спрашивает, жив ли кот Шредигера в настоящее время, не пытаясь открыть коробку.
Если я правильно понимаю, то обе проблемы были бы спорными в вашем конкретном случае благодаря блокировке мастер-мьютекса. Но это не кажется мне частым случаем, поэтому я думаю, что комитет поступил правильно, не добавив функцию, которая может быть несколько полезной в очень особых сценариях и наносить ущерб всем остальным. (В духе: «Сделайте интерфейсы простыми в использовании, правильными и трудными в использовании неправильно».)
И если я могу сказать, я думаю, что у вас сейчас не самая элегантная настройка, и ее можно было бы изменить, чтобы вообще избежать этой проблемы. Например, вместо основного потока, проверяющего всех потенциальных родителей на наличие одного, который в настоящее время не заблокирован, почему бы не поддерживать очередь готовых родителей? Если поток хочет оптимизировать другой, он выталкивает следующий из очереди и, как только у него появляются новые родители, он добавляет их в очередь. Таким образом, вам даже не нужен главный поток в качестве координатора.
источник
Похоже, что вы используете вторичные мьютексы не для того, чтобы заблокировать доступ к проблеме оптимизации, а чтобы определить, оптимизируется ли сейчас проблема оптимизации или нет.
Это совершенно не нужно. У меня был бы список проблем, которые нужно оптимизировать, список проблем, которые сейчас оптимизируются, и список проблем, которые были оптимизированы. (Не воспринимайте «список» буквально, подразумевайте «любую подходящую структуру данных»).
Операции добавления новой проблемы в список неоптимизированных проблем или перемещения проблемы из одного списка в другой будут выполняться под защитой одного «главного» мьютекса.
источник
std::mutex
подходит для такой структуры данных?std::mutex
опирается на реализацию мьютекса, определяемую операционной системой, которая вполне может принимать ресурсы (например, дескрипторы), которые ограничены и медленны в распределении и / или обработке Использование одного мьютекса для блокировки доступа к внутренней структуре данных, вероятно, будет гораздо более эффективным и, возможно, более масштабируемым.Как уже говорили другие, не существует варианта использования
is_locked
мьютекса, поэтому функция не существует.Случай у вас возникли проблемы с невероятно распространенным, это в основном то , что рабочие потоки делают, которые являются одним из, если не в наиболее общей реализации потоков.
У вас есть полка с 10 ящиками на нем. У вас есть 4 рабочих, работающих с этими коробками. Как убедиться, что 4 рабочих работают на разных боксах? Первый рабочий снимает коробку с полки, прежде чем начать над ней работать. Второй рабочий видит 9 полок на полке.
Нет мьютексов для блокировки блоков, поэтому не нужно видеть состояние воображаемого мьютекса на блоке, а злоупотреблять мьютексом как логическим значением просто неправильно. Мьютекс запирает полку.
источник
В дополнение к двум причинам, приведенным в ответе 5gon12eder выше, я хотел бы добавить, что это не является ни необходимым, ни желательным.
Если вы уже держите мьютекс, вам лучше знать, что вы его держите! Вам не нужно спрашивать. Как и в случае владения блоком памяти или любым другим ресурсом, вы должны точно знать, владеете ли вы им, и когда это уместно, чтобы освободить / удалить ресурс.
Если это не так, ваша программа разработана плохо, и вы столкнулись с проблемами.
Если вам нужен доступ к общему ресурсу, защищенному мьютексом, и вы еще не держите мьютекс, вам нужно приобрести мьютекс. Другого варианта нет, иначе ваша логика программы не верна.
Вы можете найти блокировку приемлемой или неприемлемой, в любом случае,
lock()
или выtry_lock()
получите желаемое поведение. Все, что вам нужно знать, положительно и без сомнения, это то, успешно ли вы приобрели мьютекс (возвращаемое значениеtry_lock
говорит вам). Неважно, держит ли его кто-то другой или у вас есть ложный сбой.Во всех остальных случаях, прямо, это не ваше дело. Вам не нужно знать, и вы не должны знать, или делать предположения (для проблем своевременности и синхронизации, упомянутых в другом вопросе).
источник
try_lock
использовать первый ресурс, а в случае неудачи попробуйте второй. Пример: три постоянных пула соединения с сервером базы данных, и вам нужно использовать одно для отправки команды.is_locked()
может способствовать такому поведению.is_locked
, то существует гораздо лучшее решение вашей проблемы, чем то, которое вы имеете в виду.Возможно, вы захотите использовать atomic_flag с порядком памяти по умолчанию. Он не имеет данных гонки и никогда не генерирует исключений, как mutex делает с несколькими вызовами разблокировки (и прерывает бесконтрольно, я мог бы добавить ...). В качестве альтернативы, есть атомарное (например, атомарное [bool] или атомарное [int] (с треугольными скобками, а не [])), которое имеет приятные функции, такие как load и compare_exchange_strong.
источник
Я хочу добавить вариант использования для этого: это позволило бы внутренней функции гарантировать в качестве предварительного условия / утверждения, что вызывающая сторона действительно удерживает блокировку.
Для классов с несколькими такими внутренними функциями и, возможно, многими открытыми функциями, вызывающими их, это может гарантировать, что кто-то, добавляющий другую открытую функцию, вызывающую внутреннюю, действительно получит блокировку.
источник