Common Intermediate Language и системное программирование в Microsoft.Net. Макаров А.В - 124 стр.

UptoLike

Собственно работа с критическими секциями сводится к двум основ-
ным функциям: функция 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] );