Как часто mutex::lock() проверяет разблокированное состояние, если оно уже заблокировано другим потоком?

Согласно cppreference, создание std::lock_guard с параметром std::mutex вызывает метод lock() из этого mutex.

Согласно cplusplus относительно метода mutex lock():

Если мьютекс заблокирован другим потоком, выполнение вызывающего потока блокируется до тех пор, пока не будет разблокировано другим потоком...

Я не уверен, правильно ли сформулирован главный вопрос, поэтому я поместил его в контекст приведенного ниже кода.

Я хотел проверить это и посмотреть, действительно ли вызывающий поток ожидает разблокировки вместо прекращения выполнения своего вызываемого объекта (например, функции, функтора, лямбды) и/или выдает исключение. В следующем коде есть два потока t1 и t2, каждый из которых создан с помощью указателя на одну и ту же функцию foo. Каждый вызов foo будет sleep_for через определенное время, определяемое unsigned параметром num foo, перед выполнением кода, защищенного блокировкой. Сам код, защищенный блокировкой, содержит еще один период sleep_for, чтобы сделать любой заблокированный период выполнения более очевидным:

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::mutex m;

void foo(unsigned num) {
    std::this_thread::sleep_for(std::chrono::milliseconds(num * 10));
    std::lock_guard<std::mutex> guard(m);
    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
    std::cout << num << std::endl;
}

int main() {
    std::thread t1(foo, 10);
    std::thread t2(foo, 5);
    t1.join();
    t2.join();
}

Выход консоли:

5
10

Для вывода 5 требуется около/не менее 3,05 секунд. Для вывода 10 требуется около/по крайней мере еще 3 секунды. Это означает, что t2 сначала выполняет защищенный код, поскольку у него меньше времени ожидания до блокировки mutex.

Я предполагаю, что когда вызов foo из потока t1 доходит до строки lock_guard и обнаруживает, что mutex уже заблокирован t2, t1 не прекращает выполнение и не генерирует исключение. t1 просто ждет, пока его разблокируют.

Как часто std::mutex::lock() или std::lock_guard делают эту проверку для разблокировки? Насколько дорогая проверка? Проверка реализована следующим образом?

while (some_mutex.try_lock() == false) {
    std::this_thread::sleep_for(std::chrono::milliseconds(1))
}
// execute lock-protected code

person CodeBricks    schedule 10.01.2014    source источник


Ответы (2)


Как часто std::mutex::lock() или std::lock_guard выполняют эту проверку на разблокировку?

Это не так. Он блокируется внутри операционной системы до тех пор, пока ресурс не будет освобожден. Любая операционная система, которая реализовала это с помощью вращения, стала бы поводом для жалоб.

person user207421    schedule 10.01.2014
comment
Каким образом без вращения std::mutex::lock() можно заставить блокироваться почти сразу после того, как другой поток освободил свою блокировку? - person CodeBricks; 10.01.2014
comment
Путем планирования. То же, как если бы поток стал работоспособным по любой другой причине, например. истечения срока ожидания, завершения ввода-вывода или даже просто повторного планирования после отмены планирования. - person user207421; 10.01.2014
comment
Под «Это не… Любая операционная система, реализующая это с помощью вращения, может вызвать жалобу», вы имеете в виду: «Это возможно, но это будет плохой дизайн»? В ответе Lightness Races in Orbit говорится, что это определяется ОС, а не реализовано или не указано стандартом. - person CodeBricks; 10.01.2014
comment
«Повод для жалобы»? Это было бы полной катастрофой, если бы разрушилась основная цель упреждающих многозадачных конструкций ОС — сопоставлять запросы с теми ресурсами, которые могут их обработать. Вопрос OP отображает слишком распространенное заблуждение о функциональности ядра современной ОС. - person Martin James; 10.01.2014
comment
CodeBricks Я имел в виду нечто значительно более сильное. См. комментарий @MartinJames, с которым я полностью согласен. Ваш вопрос в основном соломенный человек, как ни странно. - person user207421; 10.01.2014
comment
Есть разница между возможной реализацией, «запрещенной стандартом» и «не запрещенной стандартом, но никогда не реализованной на практике». Я просто хочу знать, какой именно. Я перефразирую вопрос в своем предыдущем комментарии: «Запрещает ли стандарт вращающиеся реализации?» - person CodeBricks; 11.01.2014
comment
Поскольку C++ не предоставляет реализацию, он не может запретить какую-либо схему реализации. - person Martin James; 11.01.2014

Мьютексы обычно предоставляются ОС, а это означает, что за все это отвечает модель потоков вашей ОС. Эти детали вообще не специфицированы и даже не реализованы в C++.

Таким образом, в некоторой степени это будет зависеть от ряда факторов, таких как загрузка ЦП во всех процессах, относительный приоритет процесса, относительный приоритет потока...

Слишком много всего происходит, чтобы дать вам четкий ответ, даже если это было бы полезно.

person Lightness Races in Orbit    schedule 10.01.2014
comment
@EJP: ерунда. На каком-то уровне абстракции происходит вращение, пусть даже на уровне транзистора. Не существует такой вещи, как волшебная программа, которая внезапно оживает, когда выполняется какое-то условие без проверки этого условия. Но так как это, вероятно, глубоко внутри ОС, не стоит рассуждать об этом. - person Lightness Races in Orbit; 10.01.2014
comment
@LightnessRacesinOrbit ошибка .. за исключением концепции аппаратных прерываний, которые лежат в основе всех современных вытесняющих многозадачных ОС. Никуда крутиться не надо. Бездействующая система без текущих входов ничего не делает. Ядра застряли на инструкции «остановка», ожидая прерывания. Инструкции не извлекаются, инструкции не выполняются, шины простаивают, ничего не происходит. Не крутится, логические элементы не меняют состояние, транзисторы не включаются и не выключаются, ничего. Нет спиннинга. Это не глубоко в ОС, это глубоко в оборудовании. - person Martin James; 11.01.2014
comment
@MartinJames: Я соглашусь с вами - то, что я хотел сказать, было глубоко внутри компьютера. Я недостаточно хорошо разбираюсь в прерываниях, чтобы дать точную информацию о них, но на каком-то уровне что-то должно вращаться, верно? Даже если на этом уровне это совсем не такое вращение, которое мы описали бы в программном обеспечении? - person Lightness Races in Orbit; 11.01.2014
comment
@LightnessRacesinOrbit - нет, не совсем, все ядра могут быть полностью остановлены, при этом ничего не происходит, пока не будет подтвержден входной контакт / линия аппаратного прерывания. Когда это происходит (просто логические вентили, без вращения :), аппаратное обеспечение контроллера прерываний следует шинному протоколу подтверждения прерывания, чтобы определить, какое периферийное устройство выдвинуло прерывание, выбирает соответствующий вектор прерывания из таблицы, загружает счетчик базовой программы с помощью это и запускает выпуск этого ядра из остановленного состояния. Обычно вектор указывает на точку входа драйвера устройства. - person Martin James; 12.01.2014
comment
@MartinJames: Что-то должно вызвать прерывание. Однако на самом деле существует множество сценариев освобождения мьютекса, которые не связаны с периферийной активностью... - person Lightness Races in Orbit; 12.01.2014