ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 02.04.2021

Просмотров: 267

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

198

Глава 5. Синхронизация параллельно выполняемых задач

Окончание табл. 5.3

Прототипы функций

Описание

int
 pthread_mutexattr_settype
  (pthread_mutexattr_t * attr,
   int type);

int
 pthread_mutexattr_gettype
  (const pthread_mutexattr_t *
                restrict attr,
   int *restrict type);

Устанавливает и возвращает атрибут мьютекса

type

 мьютексного атрибутного объекта, за

данного параметром 

attr

. Атрибут мьютекса

type

 позволяет определить, будет ли мьютекс

распознавать взаимоблокировку, проверять
ошибки и т.д. Параметр 

type

 может содер

жать такие значения:

PTHREAD_MUTEX_DEFAULT
PTHREAD_MUTEX_RECURSIVE
PTHREAD_MUTEX_ERRORCHECK
PTHREAD_MUTEX_NORMAL

Самый большой интерес представляет установка атрибута, связанного с тем, каким

должен быть мьютекс: закрытым или разделяемым. 

Закрытые

 мьютексы разделяются

между потоками одного процесса. Можно либо объявить мьютекс глобальным, либо
организовать передачу дескриптора между потоками. 

Разделяемые

 мьютексы исполь

зуются потоками, имеющими доступ к памяти, в которой размещен данный мьютекс.
Такой мьютекс могут разделять потоки различных процессов. Принцип действия за
крытого и разделяемого мьютексов показан на рис. 5.5. Если разделять мьютекс при
ходится потокам различных процессов, его необходимо разместить в памяти, которая
является общей для этих процессов. В библиотеке POSIX определено несколько
функций, предназначенных для распределения памяти между объектами с помощью
отображаемых в памяти файлов и объектов разделяемой памяти. В процессах мью
тексы можно использовать для защиты критических разделов, которые получают дос
туп к файлам, каналам, общей памяти и внешним устройствам.

5.3.2.2. Использование мьютексных семафоров для управления

критическими разделами

Мьютексы используются для управления критическими разделами процессов и по

токов, чтобы предотвратить возникновение условий “гонок”. Мьютексы позволяют
избежать условий “гонок”, реализуя последовательный доступ к критическому разде
лу. Рассмотрим код листинга 5.1. В нем демонстрируется выполнение двух потоков.
Для защиты их критических разделов и используются мьютексы.

// 

Листинг

 5.1. 

Использование

 

мьютексов

 

для

 

защиты

//              

критических

 

разделов

 

потоков

// ...
pthread_t ThreadA,ThreadB;
pthread_mutex_t Mutex;
pthread_mutexattr_t MutexAttr;

void *task1(void *X)
{
  pthread_mutex_lock(&Mutex);
  // 

Критический

 

раздел

 

кода

.

  pthread_mutex_unlock(&Mutex);


background image

5.3. Что такое семафоры

199

  return(0);
}

void *task2(void *X)
{
  pthread_mutex_lock(&Mutex);
  // 

Критический

 

раздел

 

кода

.

  pthread_mutex_unlock(&Mutex);
  return(0);
}

int main(void)
{
   //...
   pthread_mutexattr_init(&MutexAttr);
   pthread_mutex_init(&Mutex,&MutexAttr);
   //

Устанавливаем

 

атрибуты

 

мьютекса

.

   pthread_create(&ThreadA,NULL,task1,NULL);
   pthread_create(&ThreadB,NULL,task2,NULL);
   //...
   return(0);
}

В листинге 5.1 потоки 

ThreadA

 и 

ThreadB

 содержат критические разделы, защи

щаемые с помощью объекта 

Mutex

.

В листинге 5.2 демонстрируется, как можно использовать мьютексы для защиты

критических разделов процессов.

// 

Листинг

 5.2. 

Использование

 

мьютексов

 

для

 

защиты

//              

критических

 

разделов

 

процессов

//...
int Rt;
pthread_mutex_t Mutex1;
pthread_mutexattr_t MutexAttr;

int main(void)
{
   //...
   pthread_mutexattr_init(&MutexAttr);
   pthread_mutexattr_setpshared(&MutexAttr,
                                PTHREAD_PROCESS_SHARED);
   pthread_mutex_init(&Mutex1,&MutexAttr);
   if((Rt = fork()) == 0){ // 

Сыновний

 

процесс

.

      pthread_mutex_lock(&Mutex1);
      // 

Критический

 

раздел

.

      pthread_mutex_unlock(&Mutex1);
   }
   else{ // 

Родительский

 

процесс

.

      pthread_mutex_lock(&Mutex1);
      // 

Критический

 

раздел

.

      pthread_mutex_unlock(&Mutex1);
   }
   //...
   return(0);
}


background image

200

Глава 5. Синхронизация параллельно выполняемых задач

Поток A 

Поток C 

Закрытый

мьютекс

Поток A

Поток C 

Запись 

Запись 

АДРЕСНОЕ ПРОСТРАНСТВО ПРОЦЕССА A

Разделяемый

мьютекс

Поток A 

Поток C 

Запись 

Запись 

АДРЕСНОЕ ПРОСТРАНСТВО ПРОЦЕССА B

Запись 

Запись 

АДРЕСНОЕ ПРОСТРАНСТВО

ПРОЦЕССА A

Поток A 

Поток C 

Чтение

Чтение

АДРЕСНОЕ ПРОСТРАНСТВО

ПРОЦЕССА B

Разделяемый

мьютекс

Рис. 5.5.

 Закрытые и разделяемые мьютексы

Важно отметить, что в листинге 5.2 при вызове следующей функции мьютекс ини

циализируется как разделяемый:

pthread_mutexattr_setpshared(&MutexAttr,
                             PTHREAD_PROCESS_SHARED);


background image

5.3. Что такое семафоры

201

Установка этого атрибута равным значению 

PTHREAD_PROCESS_SHARED

 позволяет

объекту 

Mutex

 стать разделяемым между потоками различных процессов. После вы

зова функции 

fork()

 сыновний и родительский процессы могут защищать свои кри

тические разделы с помощью объекта 

Mutex

. Критические разделы этих процессов

могут содержать некоторые ресурсы, разделяемые обоими процессами.

5.3.3. Блокировки для чтения и записи

Мьютексные семафоры позволяют управлять критическими разделами, обеспе

чивая последовательный вход в эти разделы. В любой момент времени вход в кри
тический раздел разрешается только одному потоку или процессу. Реализуя блоки
ровки для чтения и записи, можно разрешить вход в критический раздел сразу не
скольким потокам, если они намерены лишь считывать данные из разделяемой
памяти. Следовательно, блокировкой для чтения может владеть любое количество
потоков. Но если сразу несколько потоков должны записывать или модифициро
вать данные общей памяти, то доступ для этого будет предоставлен только одному
потоку. Другими словами, никаким другим потокам не будет разрешено входить
в критический раздел, если одному потоку предоставлен монопольный доступ для
записи в разделяемую память. Такой подход может оказаться полезным, если при
ложения чаще считывают данные, чем записывают их. Если в приложении создает
ся множество потоков, организация взаимно исключающего доступа может ока
заться излишней предосторожностью. Производительность такого приложения
может значительно увеличиться, если в нем разрешить одновременное считывание
данных несколькими потоками. Стандарт POSIX определяет механизм блокировки
для чтения и записи посредством типа 

pthread_rwlock_t

.

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

мафоры. Они перечислены в табл. 5.4.

Различие между обычными мьютексами и мьютексами, обеспечивающими чтение

и запись, заключается в операциях запроса на блокирование. Вместо одной операции
блокирования здесь предусмотрено две:

pthread_rwlock_rdlock()
pthread_rwlock_wrlock()

Функция 

pthread_rwlock_rdlock()

 предоставляет вызывающему потоку блоки

ровку чтения, а функция 

pthread_rwlock_wrlock()

 — блокировку записи. Запросив

блокировку чтения, поток получит ее в том случае, если нет потоков, удерживающих
блокировку записи. Если же таковые имеются, вызывающий поток блокируется. Если
поток запросит блокировку записи, он ее получит в том случае, если нет потоков,
удерживающих блокировку чтения или блокировку записи. Если же таковые имеются,
вызывающий поток блокируется.

Блокировка чтениязаписи реализуется с помощью объектов типа

pthread_rwlock_t

. Этот же тип имеет атрибутный объект, который инкапсули

рует атрибуты объекта блокировки. Функции установки и чтения атрибутов пере
числены в табл. 5.5.

Объект типа 

pthread_rwlock_t

 может быть закрытым (для разделения между

потоками одного процесса) или разделяемым (для разделения между потоками раз
личных процессов).


background image

202

Глава 5. Синхронизация параллельно выполняемых задач

Таблица 5.4. Операции, используемые для блокировки чтения-записи

Операции

Прототипы функций

#include <pthread.h>

Инициализация

int pthread_rwlock_init(
  pthread_rwlock_t *restrict rwlock,
  const pthread_rwlockattr_t *restrict attr);

Запрос на блокировку

#include <time.h>

int pthread_rwlock_rdlock(
               pthread_rwlock_t *rwlock);

int pthread_rwlock_wrlock(
               pthread_rwlock_t *rwlock);

int pthread_rwlock_timedrdlock(
       pthread_rwlock_t *restrict rwlock,
       const struct timespec *restrict
                              abs_timeout);

int pthread_rwlock_timedwrlock(
       pthread_rwlock_t | *restrict rwlock,
       const struct timespec *restrict
                              abs_timeout);

Освобождение
блокировки

int pthread_rwlock_unlock(
                pthread_rwlock_t *rwlock);

Тестирование
блокировки

int pthread_rwlock_tryrdlock(
                  pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(
                  pthread_rwlock_t *rwlock);

Разрушение

int pthread_rwlock_destroy(
                  pthread_rwlock_t *rwlock);

Таблица 5.5. Функции доступа к атрибутному объекту типа 

pthread_rwlock_t

Прототипы функций

Описание

#include <pthread.h>

int pthread_rwlockattr_init
 (pthread_rwlockattr_t * attr);

Инициализирует атрибутный объект блоки
ровки чтениязаписи, заданный параметром

attr

, значениями, действующими по умолча

нию для всех атрибутов, определенных реа
лизацией

int pthread_rwlockattr_destroy
 (pthread_rwlockattr_t * attr);

Разрушает атрибутный объект блокировки
чтениязаписи, заданный параметром

 

attr

.

Его можно инициализировать повторно, вы
звав функцию

pthread_rwlockattr_init()