Составители:
Рубрика:
Собственно работа с критическими секциями сводится к двум основ-
ным функциям: функция EnterCriticalSection, которая соответствует
входу в критическую секцию, при необходимости с ожиданием, не огра-
ниченным по времени (!); и функция LeaveCriticalSection, которая соот-
ветствует выходу из этой секции, возможно с пробуждением потоков,
ожидающих ее освобождения. При этом система исключает вход в крити-
ческую секцию всех остальных потоков процесса, в то время как поток,
уже вошедший в данную секцию, может входить в нее рекурсивно – надо
лишь, чтобы число выходов из секции соответствовало числу входов:
#include <stdio.h>
#include <process.h>
#define _WIN32_WINNT 0x0403
#include <windows.h>
#define THREADS 10
#define ASIZE 10000000
static LONG array[ASIZE];
static CRITICAL_SECTION CS;
unsigned __stdcall ThreadProc( void *param )
{
int i;
for ( i = 0; i < ASIZE; i++ ) {
EnterCriticalSection( &CS );
array[i]++;
LeaveCriticalSection( &CS );
}
return 0;
}
int main( void )
{
HANDLE hThread[THREADS];
unsigned dwThread;
int i, errs;
InitializeCriticalSectionAndSpinCount( &CS, 100 );
for ( i = 0; i < THREADS; i++ )
hThread[i] = (HANDLE)_beginthreadex(
NULL, 0, ThreadProc, (void*)i, 0, &dwThread );
WaitForMultipleObjects( THREADS, hThread, TRUE, INFINITE );
for ( i = 0; i < THREADS; i++ ) CloseHandle( hThread[i] );
Разработка параллельных приложений для ОС Windows
235
в том числе со сравнением (InterlockedCompareExchange,
InterlockedCompareExchangePointer).
В приведенном выше примере было бы достаточно заменить опера-
тор array[i]++ на вызов функции InterlockedIncrement:
unsigned __stdcall ThreadProc( void *param )
{
int i;
for ( i = 0; i < ASIZE; i++ ) InterlockedIncrement(array+i);
return 0;
}
Еще несколько функций предназначены для работы с односвязными
LIFO списками. Функция InitializeSListHead подготавливает начальный
указатель на LIFO список, функция InterlockedPushEntrySList добавляет но-
вую запись в список, функция InterlockedPopEntrySList извлекает из списка
последнюю добавленную запись и функция InterlockedFlushSList очищает
список. Операции изменения указателей в списке являются атомарными.
7.2.1.2. Критические секции
Термин «критическая секция» обозначает некоторый фрагмент кода,
который должен выполняться в исключительном режиме – никакие дру-
гие потоки и процессы не должны выполнять этот же фрагмент в то же
время. В некоторых операционных системах для однопроцессорных ма-
шин вход в критическую секцию просто блокирует работу планировщика
до выхода из секции. Этот подход не всегда эффективен: нахождение по-
тока в критической секции не должно влиять на возможность исполнения
кода, не принадлежащего именно данной критической секции, другими
потоками, особенно на многопроцессорных машинах.
В Windows предусмотрен специальный тип данных, называемый
CRITICAL_SECTION и предназначенный для реализации критических сек-
ций. В приложении может существовать произвольное количество данных
этого типа, реализующих различные критические секции; равно как
несколько секций кода могут использовать один общий объект
CRITICAL_SECTION.
Существуют четыре основных функции для работы с критическими
секциями; перед использованием критическая секция должна быть ини-
циализирована с помощью функции InitializeCriticalSection. Объект
CRITICAL_SECTION, принадлежащий пользовательскому процессу, может
использовать в своей реализации объекты ядра для ожидания; эти объек-
ты могут создаваться по мере надобности, и для окончательного освобож-
дения ресурсов, после использования критической секции она должна
быть удалена с помощью функции DeleteCriticalSection.
234
CIL и системное программирование в Microsoft .NET
234 CIL и системное программирование в Microsoft .NET Разработка параллельных приложений для ОС Windows 235 в том числе со сравнением (InterlockedCompareExchange, Собственно работа с критическими секциями сводится к двум основ- InterlockedCompareExchangePointer). ным функциям: функция EnterCriticalSection, которая соответствует В приведенном выше примере было бы достаточно заменить опера- входу в критическую секцию, при необходимости с ожиданием, не огра- тор array[i]++ на вызов функции InterlockedIncrement: ниченным по времени (!); и функция LeaveCriticalSection, которая соот- ветствует выходу из этой секции, возможно с пробуждением потоков, unsigned __stdcall ThreadProc( void *param ) ожидающих ее освобождения. При этом система исключает вход в крити- { ческую секцию всех остальных потоков процесса, в то время как поток, int i; уже вошедший в данную секцию, может входить в нее рекурсивно – надо for ( i = 0; i < ASIZE; i++ ) InterlockedIncrement(array+i); лишь, чтобы число выходов из секции соответствовало числу входов: return 0; } #includeЕще несколько функций предназначены для работы с односвязными #include LIFO списками. Функция InitializeSListHead подготавливает начальный #define _WIN32_WINNT 0x0403 указатель на LIFO список, функция InterlockedPushEntrySList добавляет но- #include вую запись в список, функция InterlockedPopEntrySList извлекает из списка последнюю добавленную запись и функция InterlockedFlushSList очищает #define THREADS 10 список. Операции изменения указателей в списке являются атомарными. #define ASIZE 10000000 static LONG array[ASIZE]; 7.2.1.2. Критические секции static CRITICAL_SECTION CS; Термин «критическая секция» обозначает некоторый фрагмент кода, который должен выполняться в исключительном режиме – никакие дру- unsigned __stdcall ThreadProc( void *param ) гие потоки и процессы не должны выполнять этот же фрагмент в то же { время. В некоторых операционных системах для однопроцессорных ма- int i; шин вход в критическую секцию просто блокирует работу планировщика for ( i = 0; i < ASIZE; i++ ) { до выхода из секции. Этот подход не всегда эффективен: нахождение по- EnterCriticalSection( &CS ); тока в критической секции не должно влиять на возможность исполнения array[i]++; кода, не принадлежащего именно данной критической секции, другими LeaveCriticalSection( &CS ); потоками, особенно на многопроцессорных машинах. } В Windows предусмотрен специальный тип данных, называемый return 0; CRITICAL_SECTION и предназначенный для реализации критических сек- } ций. В приложении может существовать произвольное количество данных этого типа, реализующих различные критические секции; равно как int main( void ) несколько секций кода могут использовать один общий объект { CRITICAL_SECTION. HANDLE hThread[THREADS]; Существуют четыре основных функции для работы с критическими unsigned dwThread; секциями; перед использованием критическая секция должна быть ини- int i, errs; циализирована с помощью функции InitializeCriticalSection. Объект InitializeCriticalSectionAndSpinCount( &CS, 100 ); CRITICAL_SECTION, принадлежащий пользовательскому процессу, может for ( i = 0; i < THREADS; i++ ) использовать в своей реализации объекты ядра для ожидания; эти объек- hThread[i] = (HANDLE)_beginthreadex( ты могут создаваться по мере надобности, и для окончательного освобож- NULL, 0, ThreadProc, (void*)i, 0, &dwThread ); дения ресурсов, после использования критической секции она должна WaitForMultipleObjects( THREADS, hThread, TRUE, INFINITE ); быть удалена с помощью функции DeleteCriticalSection. for ( i = 0; i < THREADS; i++ ) CloseHandle( hThread[i] );
Страницы
- « первая
- ‹ предыдущая
- …
- 122
- 123
- 124
- 125
- 126
- …
- следующая ›
- последняя »