Вопрос

Я отлаживал многопоточное приложение и обнаружил внутреннюю структуру CRITICAL_SECTION.Я нашел элемент данных LockSemaphore CRITICAL_SECTION интересный.

Это выглядит как LockSemaphore это событие с автоматическим сбросом (а не семафор, как следует из названия), и операционная система создает это событие автоматически, когда поток впервые ожидает Critcal Section который заблокирован каким-то другим потоком.

Теперь мне интересно, всегда ли критическая секция быстрее?Событие — это объект ядра, и каждый объект критической секции связан с объектом события. Critical Section может быть быстрее по сравнению с другими объектами ядра, такими как Mutex?Кроме того, как объект внутреннего события влияет на производительность критического раздела?

Вот структура CRITICAL_SECTION:

struct RTL_CRITICAL_SECTION
{
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;
};
Это было полезно?

Решение

Когда они говорят, что критический раздел «быстр», они имеют в виду, что «дешево приобрести его, когда он еще не заблокирован другим потоком».

[Обратите внимание, что если это является уже заблокирован другим потоком, то не имеет большого значения, насколько он быстрый.]

Причина его скорости заключается в том, что перед входом в ядро ​​он использует эквивалент InterlockedIncrement на одном из тех LONG поле (возможно, на LockCount поле) и если это удается, то он считает блокировку полученной без обращения к ядру.

А InterlockedIncrement API, я думаю, реализован в пользовательском режиме как код операции «LOCK INC»...другими словами, вы можете получить неоспоримую критическую секцию, вообще не совершая кольцевого перехода в ядро.

Другие советы

В работе над производительностью мало что попадает в категорию «всегда» :) Если вы сами реализуете что-то похожее на критическую секцию ОС, используя другие примитивы, то, скорее всего, в большинстве случаев это будет медленнее.

Лучший способ ответить на ваш вопрос — это измерить производительность.Как работают объекты ОС очень в зависимости от сценария.Например, критические разделы обычно считаются «быстрыми», если конфликты невелики.Они также считаются быстрыми, если время блокировки меньше времени отсчета вращения.

Самое важное, что необходимо определить, — является ли конкуренция в критическом разделе ограничивающим фактором первого порядка в вашем приложении.Если нет, то просто используйте критический раздел в обычном режиме и работайте над основным узким местом (или шеями) вашего приложения.

Если производительность критической секции имеет решающее значение, вы можете рассмотреть следующее.

  1. Тщательно установите количество спин-блокировок для «горячих» критических секций.Если производительность имеет первостепенное значение, то работа здесь того стоит.Помните, что хотя спин-блокировка позволяет избежать перехода из пользовательского режима в режим ядра, она потребляет время ЦП с бешеной скоростью — во время вращения ничто другое не может использовать это время ЦП.Если блокировка удерживается достаточно долго, вращающийся поток фактически блокируется, освобождая этот процессор для выполнения другой работы.
  2. Если у вас есть шаблон чтения/записи, рассмотрите возможность использования Тонкие замки для чтения/записи (SRW).Обратной стороной является то, что они доступны только в продуктах Vista и Windows Server 2008 и более поздних версий.
  3. Возможно, вы сможете использовать переменные условия с вашим критическим разделом, чтобы свести к минимуму опросы и конфликты, пробуждая потоки только при необходимости.Опять же, они поддерживаются в продуктах Vista и Windows Server 2008 и более поздних версий.
  4. Рассмотрите возможность использования Взаимосвязанные односвязные списки (SLIST) — они эффективны и не требуют блокировки.Более того, они поддерживаются в продуктах XP и Windows Server 2003 и более поздних версиях.
  5. Изучите свой код — возможно, вам удастся снять «горячую» блокировку, проведя рефакторинг некоторого кода и используя взаимосвязанную операцию или SLIST для синхронизации и связи.

Подводя итог, можно сказать, что настройка сценариев, в которых возникает конфликт блокировок, может оказаться сложной (но интересной!) работой.Сосредоточьтесь на измерении производительности ваших приложений и понимании того, где находятся ваши горячие пути.Инструменты xperf в Набор инструментов производительности Windows здесь ваш друг :) Мы только что выпустили версию 4.5 в Microsoft Windows SDK для Windows 7 и .NET Framework 3.5 SP1 (ИСО здесь, веб-установщик здесь).Вы можете найти форум инструментов xperf. здесь.V4.5 полностью поддерживает Win7, Vista, Windows Server 2008 — все версии.

CriticalSections быстрее, но БлокировкаПриращение/БлокированноеДекремент Больше.См. этот пример использования реализации. Полная копия LightweightLock.

CriticalSections будет вращаться некоторое время (несколько мс) и продолжать проверять, свободна ли блокировка.После того, как счетчик вращений истечет, он вернется к событию ядра.Таким образом, в случае, когда держатель блокировки быстро высвобождается, вам никогда не придется делать дорогостоящий переход к коду ядра.

РЕДАКТИРОВАТЬ:Пошел и нашел несколько комментариев в моем коде:очевидно, MS Heap Manager использует счетчик вращений 4000 (целочисленные приращения, а не мс)

Вот как это можно посмотреть:

Если нет конфликтов, то спин-блокировка выполняется очень быстро по сравнению с переходом в режим ядра для мьютекса.

В случае конфликта использование CriticalSection немного дороже, чем непосредственное использование Mutex (из-за дополнительной работы по обнаружению состояния спин-блокировки).

Таким образом, все сводится к средневзвешенному значению, где веса зависят от особенностей вашего шаблона звонков.При этом, если у вас мало разногласий, то CriticalSection — это большая победа.С другой стороны, если у вас постоянно возникает много конфликтов, вам придется заплатить очень небольшой штраф за прямое использование мьютекса.Но в этом случае выгода от переключения на мьютекс невелика, поэтому вам, вероятно, лучше попытаться уменьшить конфликт.

Критический раздел работает быстрее, чем мьютекс, потому что критический раздел не является объектом ядра.Это часть глобальной памяти текущего процесса.Мьютекс на самом деле находится в ядре, и для создания объекта мьютекса требуется переключатель ядра, но в случае критического раздела — нет.Несмотря на то, что критическая секция работает быстро, при использовании критической секции произойдет переключение ядра, когда потоки перейдут в состояние ожидания.Это связано с тем, что планирование потоков происходит на стороне ядра.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top