K>"Для использования спин-блокировки в критической секции нужно инициализи ровать счетчик циклов, вызвав:
K>BOOL InitalizeCriticalSectionAndSpinCount( PCRITICAL_SECTION pcs, DWORD dwSpinCount);
K>Как и в InitializeCriticalSection, первый параметр этой функции — адрес структуры критической секции. Но во втором параметре, dwSpinCount, передается число циклов спин-блокировки при попытках получить доступ к ресурсу до перевода потока в состояние ожидания. Этот параметр может принимать значения от 0 до 0x00FFFFFF. Учтите, что на однопроцессорной машине значение параметра dwSpinCount игнорируется и считается равным 0. Дело в том, что применение спин-блокировки в такой системе бессмысленно: поток, владеющий ресурсом, не сможет освободить его, пока другой поток «крутится» в циклах спин-блокировки."
K> Не понятно почему на однопроцессорной машине, пока один поток крутиться в цикле, другой не сможет освободить ресурс. Если у нах одиноковый приоритет, то зацикленный поток по окончанию своего кванта прервется и другой поток сможет освободить ресурс(хотя смотря на чем прервался, наверное). При чем здесь кол-во процессоров?
Попробую я тоже объяснить своими словами. Цель введения спин-блокировки — увеличить производительность на многопроцессорных машинах. Дело в том, что переход потока в состояние ожидания и следующее за ним переключение контекста между потоками — это довольно длительная операция, сотни, если не тысячи инструкций процессора. В то же время фрагмент кода, защищаемый критической секцией, зачастую бывает очень небольшим, например, вставка элемента в двусвязный список занимает не больше десятка инструкций.
Каким же образом введение задержки может повысить производительность? Рассмотрим простой пример:
EnterCriticalSection(&g_crit);
InsertTailList(&g_ListHead, pEntry);
LeaveCriticalSection(&g_crit);
Допустим, этот код выполняется на двухпроцессорной машине и спин-блокировка не используется. Если два потока, выполняющиеся на разных процессорах, почти одновременно попытаются войти в критическую секцию, то события будут развиваться примерно следующим образом:
-+------+------------------+-------+------------------------------- -------------------------------------
| | | |
CPU 1: | Вход | Вставка элемента | Выход | ...
| | | |
-+--+---+------------------+-------+-----------------------------+- -+------+------------------+-------+-
| Неудачная попытка входа, которая заканчивается переходом в | | | | |
CPU 2: | состояние ожидания, и переключением процессора на другой | ... | Вход | Вставка элемента | Выход |
| поток | | | | |
----+------------------------------------------------------------+- -+------+------------------+-------+-
Как видно на схеме, из за того, что критическая секция была занята, второй поток был вынужден перейти в состояние ожидания, и, потенциально, CPU 2 перешел к выполнению другого потока. Только через некоторое время второй поток получит вновь управление и сможет войти в критическую секцию.
При использовании спин-блокировки ситуация иная:
-+------+------------------+-------+------------------------------
| | | |
CPU 1: | Вход | Вставка элемента | Выход |
| | | |
-+--+---+------------------+-------+-+------------------+-------+-
| Неудачная попытка входа, кото- | | |
CPU 2: | рая переходит в спин-блокиров- | Вставка элемента | Выход |
| ку | | |
----+--------------------------------+------------------+-------+-
Cпин-блокировка заканчивается сразу же как только первый поток освободил критическую секцию. Как видно из рисунка, во втором случае второй поток получает доступ к критической секции гораздо быстрее, и, что самое важное, общее число производимых операций существенно меньше, так как не включает дорогую операцию переключения контекста.
В случае с одним процессором применение спин-блокировки не имеет смысла. Если второй поток обнаруживает, что критическая секция занята, то единственный способ ее освободить — это дать выполняться первому потоку. Но у нас только один процессор, поэтому для того, чтобы первый поток смог возобновить выполнение, необходимо переключение контекста:
-+------+---------+------------------------------------------------+-
| | | Первый поток исчерпал свой time slice, поэтому |
CPU 1: | Вход | Вставка | происходит переключение контекста на второй | -->
| | | поток |
-+------+---------+------------------------------------------------+-
\ первый поток / \
-+------------------------------------------------------------+----------+-------+- -+-
| Неудачная попытка входа, которая заканчивается переходом в | | | |
--> | состояние ожидания, и переключением процессора на другой | элемента | Выход | ... | -->
| поток | | | |
-+------------------------------------------------------------+----------+-------+- -+-
/ \ второй поток / \ первый поток
-+------------------------------------------------+------+------------------+-------+-
| Первый поток исчерпал свой time slice, поэтому | | | |
--> | происходит переключение контекста на второй | Вход | Вставка элемента | Выход | ...
| поток | | | |
-+------------------------------------------------+------+------------------+-------+-
первый поток / \ второй поток
Если мы добавим спин-блокировку, то только отдалим тот момент, когда второй поток произведет неизбежное переключение контекста и первый поток освободит критическую секцию.