Системное программирование в среде Windows
Шрифт:
• Если обнаруживается, что объект CS блокирован, ECS входит в жесткий цикл (tight loop) на SMP-системах и выполняет многократное тестирование бита блокировки, не уступая процессора (разумеется, поток может быть вытеснен). Количество повторений цикла, после выполнения которых ECS прекращает дальнейшее тестирование, определяется значением спин-счетчика CS. В однопроцессорных системах тестирование прекращается немедленно; спин-счетчики используются лишь в случае SMP-систем.
• Как только ECS прекращает тестирование бита блокировки (в случае однопроцессорных систем это происходит немедленно), она входит в ядро, и поток переводится в состояние ожидания.
• Функция LeaveCriticalSectibn реализуется путем выключения бита блокировки после проверки того, что вызывающий поток действительно является владельцем CS. Кроме того, ядро должно быть также уведомлено о том, существуют ли еще другие ожидающие потоки, для чего используется функции ReleaseSemaphore.
Таким образом, в случае однопроцессорных систем объекты CS эффективны тогда, когда высока вероятность их разблокирования, что иллюстрирует вариант CS программы 9.1. Преимущества SMP-систем обусловлены тем фактом, что пока ожидающий поток выполняет цикл ожидания, управляемый спин-счетчиком, объект CS может оказаться разблокированным потоком, выполняющимся на другом процессоре.
Далее будет показано, как устанавливать значения спин-счетчиков и настраивать приложения путем выбора наиболее оптимальных значений. Еще раз подчеркнем, что спин-счетчики оказываются полезными лишь в случае SMP-систем; на однопроцессорных системах они не используются.
Установка значений спин-счетчиков
Спин-счетчики CS могут устанавливаться на стадии инициализации объектов CS или динамическим путем. В первом случае функция InitializeCriticalSection заменяется функцией InitializeCriticalSectionAndSpinCount, в которой добавлен параметр счетчика. В то же время, способа, позволяющего считать значение спин-счетчика, не существует.
Значение спин-счетчика можно в любой момент изменить.
В документации Microsoft говорится о том, что рекомендуемым для управления кучей значением спин-счетчика является 4000. Однако оптимальное значение зависит от свойств приложения, и поэтому спин-счетчики должны настраиваться индивидуально для каждого приложения, выполняющегося в реалистическом SMP-окружении. Наилучшее значение будет различным в зависимости от количества процессоров, характера приложения и тому подобного.
На Web-сайте книги находится программа TimedMutualExclusionSC. Эта программа представляет собой видоизмененный вариант уже знакомой вам программы TimedMutualExclusion, в котором значение спин-счетчика указания в качестве параметра командной строки. Вы можете запустить эту программу на своей машине для приблизительной оценки того, какое значение спин-счетчика будет наиболее приемлемым для выполнения того или иного из вариантов тестовых программ на доступных вам SMP-системах, что и предлагается сделать в упражнении 9.2.
Дросселирование семафора для уменьшения состязательности между потоками
Слишком большое количество
Если ни один из этих методов не дает желаемого улучшения, то могло бы показаться, что нет иного выхода, кроме как уменьшить количество потоков, но при этом вы будете вынуждены заставлять одиночные потоки мультиплексировать те операции, которые естественнее было бы распределить между несколькими потоками. Выход из этой ситуации обеспечивают семафоры, которые дают возможность сохранить естественную многопоточную модель, но вместе с тем свести к минимуму количество активных потоков, конкурирующих между собой. Такое решение является концептуально простым, и его можно без труда включить в любую существующую прикладную программу, например TimedMutualExclusion. В системе "хозяин/рабочий" решение, носящее название "дросселя" семафора (semaphore throttle), использует следующую методику:
• Главный поток создает семафор с небольшим, например 4, максимальным значением параметра, представляющего максимально допустимое количество активных потоков, которое, например, можно принимать равным количеству процессоров, установленных в системе, для обеспечения приемлемой производительности. Начальное значение счетчика семафора также следует установить равным максимальному значению. Это число можно сделать параметром и подбирать его оптимальное значение экспериментальным путем так же, как и в случае спин-счетчиков. Другим возможным значением этого параметра может служить количество процессоров, которое может быть определено во время выполнения программы (см. следующий раздел).
• Каждый рабочий поток ожидает перехода семафора в сигнальное состояние, прежде чем войти в критический участок кода. Ожидание семафора может непосредственно предшествовать ожиданию мьютекса или объекта CS.
• Если максимальное значение счетчика семафора равно 1, то использование мьютекса становится излишним. Подобное решение нередко является наилучшим для SMP-систем.
• Общий уровень состязательности между объектами CS или мьютексами снижается, если при сериализации выполнения потоков лишь небольшое количество потоков ожидают перехода мьютексов или объектов CS в сигнальное состояние.
Счетчик семафора просто представляет число потоков, которые могут быть активными в любой момент времени, и ограничивает количество потоков, соревнующихся между собой за право владения мьютексом, объектом CS или иным ресурсом. Главный поток даже может регулировать, или, как говорят, "дросселировать" (throttle) выполнение рабочих потоков и динамически настраивать работу приложения, ожидая, пока не уменьшится значение счетчика, если он решает, что уровень состязательности слишком высок, или увеличивая значение счетчика с помощью функции ReleaseSemaphore, чтобы дать возможность выполняться большему количеству потоков. Заметьте, однако, что максимальное значение счетчика семафора устанавливается при его создании, и изменить его впоследствии невозможно.