Блоки спина в очереди
Замки спина в очереди являются вариантом замков спина, которые хорошо работают для высококонтируемых замков. Традиционные, неуправляемые замки спина являются лучшим выбором для легко оспариваемых или более коротких блокировок длительности.
Преимущества использования блокировки спина в очереди включают:
Сокращение конфликтов процессора: традиционные блокировки спина могут привести к значительному конфликту процессора, когда несколько потоков пытаются получить блокировку одновременно, так как они непрерывно циклически (или "спин") проверка состояние блокировки. Это может снизить производительность системы, особенно в многопроцессорных системах. Блоки спина в очереди устраняют это путем организации потоков в очередь. Когда поток получает блокировку, только следующая в строке активно спинирует, ожидая получения блокировки. Это сокращает циклы ЦП, потраченные на спиннинг, особенно если блокировка удерживается в течение длительного времени.
Справедливость и избегание голода: одна из проблем с основными спин-замками является отсутствие справедливости; поток может быть голодать и никогда не получать блокировку, если другие потоки постоянно получают и освобождают его. Блокировки спина в очереди устраняют это, гарантируя, что потоки получают блокировку в том порядке, в который они пытались. Эта последовательная обработка предотвращает нехватку и гарантирует, что все потоки будут обслуживаться со временем.
Масштабируемость. По мере увеличения числа процессоров или ядер в системе эффективность механизмов синхронизации становится критической для производительности. Замки спина в очереди являются более масштабируемыми, чем традиционные замки спина, потому что они снижают нагрузку на процессоры, минимизируя активный спиннинг по всем ядрам. Это особенно важно в высокопроизводительных многоядерных системах, где эффективность драйвера может напрямую повлиять на общую производительность системы.
Эффективное использование системных ресурсов: сокращение ненужных спиновых блокировок процессора в очереди позволяет системе эффективнее использовать свои ресурсы. Это не только повышает производительность драйвера устройства, но и оказывает положительное влияние на общую скорость реагирования системы и потребление энергии, что особенно полезно в средах с учетом питания.
Простота и надежность. Несмотря на их преимущества в снижении конфликтов и повышении справедливости, очереди спин блокировки абстрагируют сложность от разработчика. Они предоставляют простой и надежный механизм защиты общих ресурсов без необходимости реализовать сложную логику блокировки. Эта простота снижает вероятность ошибок, связанных с неправильной обработкой блокировки, что повышает надежность драйвера.
Ниже приведен упрощенный фрагмент кода, демонстрирующий описанные операции с блокировкой спина в очереди в драйвере режима ядра Windows. В этом примере показано, как объявить и инициализировать блокировку спина с помощью KeInitializeSpinLock, а затем получить и освободить блокировку с помощью KeAcquireInStackQueuedSpinLock и KeReleaseInStackQueuedSpinLock соответственно.
KSPIN_LOCK SpinLock;
KLOCK_QUEUE_HANDLE LockHandle;
// Initialize the spin lock
KeInitializeSpinLock(&SpinLock);
// Assume this function is called in some kind of context where
// the below operations make sense, e.g., in a device I/O path
// Acquire the queued spin lock
KeAcquireInStackQueuedSpinLock(&SpinLock, &LockHandle);
// At this point, the current thread holds the spin lock.
// Perform thread-safe operations here.
// ...
// Release the queued spin lock
KeReleaseInStackQueuedSpinLock(&LockHandle);
Драйвер выделяет KLOCK_QUEUE_HANDLE структуру, которая передается указателем на KeAcquireInStackQueuedSpinLock. Драйвер передает ту же структуру указателем на KeReleaseInStackQueuedSpinLock при выпуске блокировки спина.
Драйверы обычно должны выделять структуру в стеке каждый раз, когда они получают блокировку. Драйвер не должен выделять структуру как часть контекста устройства, а затем совместно использовать одну и ту же структуру из нескольких потоков.
Водители не должны смешивать вызовы к подпрограммам блокировки в очереди и обычным подпрограммам KeXxxSpinLock на одной и той же спин-блокировке.
Если драйвер уже находится в IRQL = DISPATCH_LEVEL, он может вызывать KeAcquireInStackQueuedSpinLockAtDpcLevel и KeReleaseInStackQueuedSpinLockFromDpcLevel.