Окт 01

Атомарный доступ и семейство Interlocked-функций

Большая часть синхронизации потоков связана с атомарным доступом (atomic access) — монопольным захватом ресурса обращающимся к нему потоком. Win32 API предоставляет несколько функций для реализации взаимно блокированных операций. Все Interlocked-функций работают корректно только при условии, что их аргументы выровнены по границе двойного слова (DWORD).
Функция Interlockedlncrement, имеющая прототип
LONG InterlockedIncrement(LPLONG IpAddend); инкрементирует 32-разрядную переменную, адрес которой задается параметром IpAddend. Функция возвращает новое значение указанной переменной.
Функция Interlocked Decrement определена аналогично функции Interlockedlncrement, но она декрементирует 32-разрядную переменную.
Пара функций
LONG Inter!ockedExchange(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 InterlockedCompareExchangePointeKPVOID ppvDestination.
PVOID pvExchange. PVOID pvComparand);
Если значение переменной, адрес которой передается в первом параметре, совпадает со значением, передаваемым в третьем параметре, то оно заменяется значением, передаваемым во втором параметре. В 32-разрядном приложении обе функции работают с 32-разрядными значениями. В 64-разрядной программе первая функция оперирует 32-разрядными значениями, а вторая — 64-разрядными. Обе функции возвращают исходное значение переменной, заданной первым параметром.
Вернемся к нашему приложению BadCount (см. листинг 9.3), работающему некорректно из-за одновременного доступа к общей глобальной переменной g_counter из разных потоков. Имея на вооружении функции с атомарным доступом, совсем несложно заставить приложение работать правильно. Для этого нужно в реализации функции IncCounter заменить инструкцию
++g_counter: вызовом следующей функции:
InterlockedIncrement(&g_counter);
Аналогично, в реализации функции DecCounter необходимо операцию
--g_counter; заменить следующим вызовом:
InterlockedDecrement(&g_counter);
Проверьте, как будет работать на вашем компьютере программа BadCount после указанных изменений в ее исходном тексте. Модифицированное приложение, по-видимому, достойно и нового имени GoodCount!