logo search
Ответы_ОСиСП

19. Синхронизация потоков в пределах одного процесса. Критические секции. Спин-блокировки. Interlocked-функции.

Примитив синхронизации — это объект, который помогает управлять многопоточным приложением. В Windows 2000 доступны следующие основные типы примитивов синхронизации:

Критическая секция (critical section) — это небольшой участок кода, который должен использоваться только одним потоком одновременно. Если в одно время несколько потоков попытаются получить доступ к критическому участку, то контроль над ним будет предоставлен только одному из потоков, а все остальные будут переведены в состояние ожидания до тех пор, пока участок не освободится. Для использования критической секции необходимо определить переменную типа CRITICAL_SECTION:

CRITICAL_SECTION cs;

Поскольку эта переменная должна находиться в области видимости для каждого использующего ее потока, обычно она объявляется как глобальная. Эту переменную следует инициализировать до ее первого применения с помощью функции InitializeCriticalSection(&cs); Чтобы завладеть критическим участком, поток должен вызвать функцию EnterCriticalSection(&cs);

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

Конец критического участка обозначается вызовом функции LeaveCriticalSection(&cs);

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

Спин-блокировки представляют собой чрезвычайно низкоуровневое средство синхронизации, предназначенное в первую очередь для применения в многопроцессорной конфигурации с разделяемой памятью. Они обычно реализуются как атомарно устанавливаемое булево значение (истина – блокировка установлена). Аппаратура поддерживает подобные блокировки командами вида "проверить и установить".

При попытке установить спин-блокировку, если она захвачена кем-то другим, как правило, применяется активное ожидание освобождения, с постоянным опросом в цикле состояния блокировки. Естественно, при этом занимается процессор, так что спин-блокировки следует устанавливать только на очень короткое время и их владелец не должен приостанавливать свое выполнение.

Большая часть синхронизации потоков связана с атомарным доступом (atomic access) — монопольным захватом ресурса обращающимся к нему потоком. Win32 API предоставляет несколько функций для реализации взаимно блокированных операций. Все Interlocked-функций работают корректно только при условии, что их аргументы выровнены по границе двойного слова (DWORD).

Функция Interlockedlncrement, имеющая прототип LONG InterlockedIncrement(LPLONG lpAddend); инкрементирует 32-разрядную переменную, адрес которой задается параметром LpAddend. Функция возвращает новое значение указанной переменной.

Функция Interlocked Decrement определена аналогично функции Interlockedlncrement, но она декрементирует 32-разрядную переменную. Пара функций

LONG InterlockedExchange(LPLONG IpTarget. LONG Value):

PVOID InterlockedExchangePointer(PVOID* ppvTarget. PVOID pvValue):

монопольно заменяет текущее значение переменной типа LONG, адрес которой передается в первом параметре, значением, передаваемым во втором параметре. В 32-разрядном приложении обе функции работают с 32-разрядными значениями. В 64-разрядной программе первая функция оперирует 32-разрядными значениями, а вторая — 64-разрядными. Обе функции возвращают исходное значение переменной.

Следующая функция добавляет к значению переменной, адрес которой передается в первом параметре, значение, передаваемое во втором параметре:

LONG InterlockedExchangeAdd(LPLONG IpAddend. LONG Increment):

Еще две функции выполняют операцию сравнения и присваивания по результату сравнения:

LONG InterlockedCompareExchangetLPLONG IpDestination, LONG Exchange, LONG Comparand);

PVOID InterlockedCompareExchangePointer(PVOID* ppvDestination, PVOID pvExchange, PVOID pvComparand);

Если значение переменной, адрес которой передается в первом параметре, совпадает со значением, передаваемым в третьем параметре, то оно заменяется значением, передаваемым во втором параметре. В 32-разрядном приложении обе функции работают с 32-разрядными значениями. В 64-разрядной программе первая функция оперирует 32-разрядными значениями, а вторая — 64-разрядными. Обе функции возвращают исходное значение переменной, заданной первым параметром.

Работая с критическими секциями или применяя Interlocked-функции, программа не переключается в режим ядра. Поэтому эти виды синхронизации называют синхронизацией в пользовательском режиме. Она отличается высокой скоростью реализации. Главным недостатком такой синхронизации является невозможность ее применения для потоков, принадлежащих разным процессам.