Составители:
Рубрика:
• Атомарные операции.
В достаточно частых случаях необходимо обеспечить конку-
рентный доступ к какой-либо целочисленной переменной, яв-
ляющейся счетчиком. Тогда бывает достаточно просто обеспе-
чить атомарность выполнения операций увеличения, уменьше-
ния или изменения значения переменной.
• Критические секции.
В более сложном случае необходимо гарантировать, что какой-
либо фрагмент кода будет выполняться в монопольном режиме.
Критические секции могут быть использованы только в рамках
одного процесса, однако при этом критическая секция может
работать чуть быстрее, чем синхронизация с использованием
объектов ядра.
• Синхронизация с использованием объектов ядра.
Объекты ядра должны поддерживать специальный интерфейс
синхронизируемых объектов, чтобы они могли быть использо-
ваны для взаимной синхронизации потоков или процессов.
Этот интерфейс поддерживают многие объекты ядра, например,
файлы, процессы, потоки, консоли, задания и пр. Кроме того,
Windows предоставляет специальный набор объектов, предна-
значенный именно для взаимной синхронизации потоков. Объ-
екты ядра могут быть использованы как для синхронизации по-
токов в рамках одного процесса, так и для синхронизации пото-
ков в разных процессах.
• Ожидающие таймеры.
В некоторых случаях необходимо обеспечить выполнение ка-
ких-либо операций либо в заданное время, либо с определенной
периодичностью. Для решения этих задач в Windows предусмот-
рены ожидающие таймеры, которые могут использоваться или
средствами синхронизации (ожидающий таймер является объе-
ктом ядра, поддерживающим интерфейс синхронизируемых
объектов), или для вызова асинхронных процедур.
7.2.1.1. Атомарные операции
Атомарные операции обычно имеют более-менее близкое соответст-
вие с командами процессора. Так, например, они могут сводиться к опера-
циям с блокировкой шины (префикс lock) и специальным командам (ти-
па cmpxchg) процессора. ОС Windows предоставляет функции для увеличе-
ния (InterlockedIncrement, InterlockedIncrement64) или уменьшения
(InterlockedDecrement, InterlockedDecrement64) значения целочисленных
переменных и изменения их значений (InterlockedExchange,
InterlockedExchange64, InterlockedExchangeAdd, InterlockedExchangePointer),
Разработка параллельных приложений для ОС Windows
233
unsigned __stdcall ThreadProc( void *param )
{
int i;
for ( i = 0; i < ASIZE; i++ ) array[i]++;
return 0;
}
int main( void )
{
HANDLE hThread[THREADS];
unsigned dwThread;
int i, errs;
for ( i = 0; i < THREADS; i++ )
hThread[i] = (HANDLE)_beginthreadex(
NULL, 0, ThreadProc, NULL, 0, &dwThread
);
WaitForMultipleObjects( THREADS, hThread, TRUE, INFINITE );
for ( i = 0; i < THREADS; i++ ) CloseHandle( hThread[i] );
for ( errs=i=0; i<ASIZE; i++ )
if ( array[i] != THREADS ) errs++;
if ( errs ) printf(“Detected %d errors!\n”, errs );
return 0;
}
Все потоки перебирают массив в одном и том же порядке, и шанс
конфликтов при доступе к элементам массива сравнительно невелик; од-
нако, даже в такой ситуации при достаточно большом размере массива
(если массив маленький, то потоки, скорее всего, будут работать последо-
вательно и конфликтов вообще не возникнет) будут появляться ошибки.
Причина в том, что операция увеличения на 1 элемента массива на самом
деле не элементарная – она требует запроса для считывания данных из па-
мяти, увеличения значения на 1 и сохранения данных в памяти. Если по-
ток будет прерван планировщиком между операциями считывания и пос-
ледующей записи, то после возобновления его исполнения уже может ока-
заться так, что какой-либо другой поток успел изменить значение в масси-
ве, и запись значения, вычисленного на основе старого значения, будет
некорректной. На многопроцессорных машинах такие ошибки могут воз-
никать еще чаще, и при массивах меньшего размера.
Поэтому при осуществлении одновременного доступа разных пото-
ков к общим данным необходимо предпринимать специальные меры, ис-
ключающие возникновение конфликтов. В Windows предусмотрено
несколько разных способов решения подобных проблем:
232
CIL и системное программирование в Microsoft .NET
232 CIL и системное программирование в Microsoft .NET Разработка параллельных приложений для ОС Windows 233 unsigned __stdcall ThreadProc( void *param ) • Атомарные операции. { В достаточно частых случаях необходимо обеспечить конку- int i; рентный доступ к какой-либо целочисленной переменной, яв- for ( i = 0; i < ASIZE; i++ ) array[i]++; ляющейся счетчиком. Тогда бывает достаточно просто обеспе- return 0; чить атомарность выполнения операций увеличения, уменьше- } ния или изменения значения переменной. • Критические секции. int main( void ) В более сложном случае необходимо гарантировать, что какой- { либо фрагмент кода будет выполняться в монопольном режиме. HANDLE hThread[THREADS]; Критические секции могут быть использованы только в рамках unsigned dwThread; одного процесса, однако при этом критическая секция может int i, errs; работать чуть быстрее, чем синхронизация с использованием for ( i = 0; i < THREADS; i++ ) объектов ядра. hThread[i] = (HANDLE)_beginthreadex( • Синхронизация с использованием объектов ядра. NULL, 0, ThreadProc, NULL, 0, &dwThread Объекты ядра должны поддерживать специальный интерфейс ); синхронизируемых объектов, чтобы они могли быть использо- WaitForMultipleObjects( THREADS, hThread, TRUE, INFINITE ); ваны для взаимной синхронизации потоков или процессов. for ( i = 0; i < THREADS; i++ ) CloseHandle( hThread[i] ); Этот интерфейс поддерживают многие объекты ядра, например, for ( errs=i=0; i
Страницы
- « первая
- ‹ предыдущая
- …
- 121
- 122
- 123
- 124
- 125
- …
- следующая ›
- последняя »