Чтение онлайн

ЖАНРЫ

Параллельное и распределенное программирование на С++
Шрифт:

_SC_CHILD_MAX

CHILD_MAX

Максимальное количество процессов, разрешенных для UID

_SC_PRIORITY_ SCHEDULING

_POSIX_PRIORITY_ SCHEDULING

Поддерживает планирование процессов

_SC_REALTIME_ SIGNALS

_POSIX_REALTIME_SIGNALS

Поддерживает сигналы реального времени

_SC_XOPEN_REALTIME_THREADS

_XOPEN_REALTIME_ THREADS

Поддерживает группу потоковых средств реального времени X/Open POSIX

_SC_STREAM_MAX

STREAM_MAX

Определяет

количество потоков данных, которые один процесс может открыть одновременно

_SC_SEMAPHORES

_POSIX_SEMAPHORES

Поддерживает семафоры

_SC_SEM_NSEMS_MAX

SEM_NSEMS_MAX

Определяет максимальное количество семафоров, которое может иметь процесс

_SC_SEM_VALUE_MAX

SEM_VALUE_MAX

Определяет максимальное значение, которое может иметь семафор

_SC_SHARED_MEMORY_ OBJECTS

_POSIX_SHARED_MEMORY_OBJECTS

Поддерживает объекты общей памяти

Управление критическими разделами

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

Если существуют процессы или потоки, которые получают доступ к разделяемым модифицируемым данным, структурам данных или переменным, то все эти данные находятся в критической области (или разделе) кода процессов или потоков. Критический раздел кода — это та его часть, в которой обеспечивается доступ потока или процесса к разделяемому блоку модифицируемой памяти и обработка соответствующих данных. Отнесение раздела кода к критическому можно использовать для управления состоянием «гонок». Например, создаваемые в программе два потока, поток А и поток В, используются для поиска нескольких ключевых слов во всех файлах системы. Поток А просматривает текстовые файлы в каждом каталоге и записывает нужные пути в списочную структуру данных TextFiles, а затем инкрементирует переменную FileCount. Поток В выделяет имена файлов из списка TextFiles, декрементирует переменную FileCount, после чего просматривает файл на предмет поиска в нем заданных ключевых слов. Файл, который их содержит, переписывается в другой файл, и инкрементируется еще одна переменная FoundCount. К переменной FoundCount поток А доступа не имеет. Потоки А и В могут выполняться одновременно на отдельных процессорах. Поток А выполняется до тех пор, пока не будут просмотрены все каталоги, в то время как поток В просматривает каждый файл, путь к которому выделен из переменной TextFiles. Упомянутый список поддерживается в отсортированном порядке, и в любой момент его содержимое можно отобразить на экране.

Здесь возможна масса проблем. Например, поток В может попытаться выделить имя файла из списка TextFiles до того, как поток А его туда поместит. Поток В может попытаться декрементировать переменную SearchCount до того, как поток А её инкрементирует, или же оба потока

могут попытаться модифицировать эту переменную одновременно. Кроме того, во время сортировки элементов списка TextFiles поток А может попытаться записать в него имя файла, или поток В будет в это время пытаться выделить из него имя файла для выполнения своей задачи. Описанные проблемы—это примеры условий «гонок», при которых несколько потоков (или процессов) пытаются одновременно модифицировать один и тот же блок общей памяти.

Если потоки или процессы одновременно лишь читают один и тот же блок памяти, условия «гонок» не возникают. Они возникают в случае, когда несколько процессов или потоков одновременно получают доступ к одному и тому же блоку памяти, и по крайней мере один из этих процессов или потоков делает попытку модифицировать данные. Раздел кода становится критическим, когда он делает возможными одновременные попытки изменить один и тот же блок памяти. Один из способов защитить к ритический раздел — разрешить только монопольный доступ к блоку памяти. Монопольный доступ означает, что к разделяемому блоку памяти будет иметь доступ один процесс или поток в течении короткого промежутка времени, при этом всем остальным процессам или потокам запрещено (путем блокировки) входить в свои критические разделы, которые обеспечивают доступ к тому же самому блоку памяти.

Для управления условиями «гонок» можно использовать такой механизм блокировки, как взаимо - исключающий семафор , или мьютекс (mutex— сокращение от «mutual exclusion», - взаимное исключение). Мьютекс используется для блокирования критического раздела: он блокируется до входа в критический раздел, а при выходе из него - деблокируется:

Блокирование мьютекса

// Вход в критический раздел.

// Доступ к разделяемой модифицируемой памяти.

// Выход из критического раздела.

Деблокирование мьютекса

Класс pthread_mutex_t позволяет смоделировать мьютексный объект. Прежде, чем объект типа pthread_mutex_t можно будет использовать, его необходимо инициализировать. Для инициализации мьютекса используется функция pthread_mutex_init. Инициализированный мьютекс можно заблокировать деблокировать и разрушить с помощью функций pthread_mutex_lock, pthread_mutex_unlock и pthread_mutex_destroy соответственно. В программе 4.5 содержится функция, которая выполняет поиск текстовых файлов, а в программе 4.6 — функция, которая просматривает каждый текстовый файл на предмет содержания в нем заданных ключевых слов. Каждая функция выполняется потоком. Основной поток реализован в программе 4.7. Эти программы реализуют модель «изготовитель-потребитель» для делегирования задач потокам. Программа4.5 содержит поток-«изготовитель», а программа 4.6 — поток-«потребитель». Критические разделы выделены в них полужирным шрифтом.

// Программа 4.5

1 int isDirectory(string FileName)

2 {

3 struct stat StatBuffer;

4

5 lstat(FileName.c_str,&StatBuffer);

6 if((StatBuffer.st_mode & S_IFDIR) == -1)

7 {

8 cout << «could not get stats on file» << endl;

9 return(0);

10 }

11 else{

12 if(StatBuffer.st_mode & S_IFDIR){

13 return(1);

14 }

15 }

16 return(0);

17 }

18

19

20 int isRegular(string FileName)

21 {

22 struct stat StatBuffer;

23

24 lstat(FileName.c_str,&StatBuffer);

25 if((StatBuffer.st_mode & S_IFDIR) == -1)

26 {

27 cout << «could not get stats on file» << endl;

28 return(0);

29 }

30 else{

31 if(StatBuffer.st_mode & S_IFREG){

32 return(1);

33 }

34 }

35 return(0);

36 }

37

38

39 void depthFirstTraversal(const char *CurrentDir)

Поделиться с друзьями: