Поделиться через


Блоки спина в очереди

Замки спина в очереди являются вариантом замков спина, которые хорошо работают для высококонтируемых замков. Традиционные, неуправляемые замки спина являются лучшим выбором для легко оспариваемых или более коротких блокировок длительности.

Преимущества использования блокировки спина в очереди включают:

  1. Сокращение конфликтов процессора: традиционные блокировки спина могут привести к значительному конфликту процессора, когда несколько потоков пытаются получить блокировку одновременно, так как они непрерывно циклически (или "спин") проверка состояние блокировки. Это может снизить производительность системы, особенно в многопроцессорных системах. Блоки спина в очереди устраняют это путем организации потоков в очередь. Когда поток получает блокировку, только следующая в строке активно спинирует, ожидая получения блокировки. Это сокращает циклы ЦП, потраченные на спиннинг, особенно если блокировка удерживается в течение длительного времени.

  2. Справедливость и избегание голода: одна из проблем с основными спин-замками является отсутствие справедливости; поток может быть голодать и никогда не получать блокировку, если другие потоки постоянно получают и освобождают его. Блокировки спина в очереди устраняют это, гарантируя, что потоки получают блокировку в том порядке, в который они пытались. Эта последовательная обработка предотвращает нехватку и гарантирует, что все потоки будут обслуживаться со временем.

  3. Масштабируемость. По мере увеличения числа процессоров или ядер в системе эффективность механизмов синхронизации становится критической для производительности. Замки спина в очереди являются более масштабируемыми, чем традиционные замки спина, потому что они снижают нагрузку на процессоры, минимизируя активный спиннинг по всем ядрам. Это особенно важно в высокопроизводительных многоядерных системах, где эффективность драйвера может напрямую повлиять на общую производительность системы.

  4. Эффективное использование системных ресурсов: сокращение ненужных спиновых блокировок процессора в очереди позволяет системе эффективнее использовать свои ресурсы. Это не только повышает производительность драйвера устройства, но и оказывает положительное влияние на общую скорость реагирования системы и потребление энергии, что особенно полезно в средах с учетом питания.

  5. Простота и надежность. Несмотря на их преимущества в снижении конфликтов и повышении справедливости, очереди спин блокировки абстрагируют сложность от разработчика. Они предоставляют простой и надежный механизм защиты общих ресурсов без необходимости реализовать сложную логику блокировки. Эта простота снижает вероятность ошибок, связанных с неправильной обработкой блокировки, что повышает надежность драйвера.

Ниже приведен упрощенный фрагмент кода, демонстрирующий описанные операции с блокировкой спина в очереди в драйвере режима ядра 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.