Литмир - Электронная Библиотека

}

В листинге 5.1 потоки ThreadA и ThreadB содержат критические разделы, защищаемые с помощью объекта Mutex.

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

// Листинг 5.2. Использование мьютексов для зашиты

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

//...

int Rt;

pthread_mutex_t Mutexl ; pthread_mutexattr_t MutexAttr;

int main(void) {

//...

pthread_mutexattr_init (&MutexAttr); pthread_mutexattr_setpshared(

&MutexAttr,

PTHREAD_PROCESS_SHARED ) ;

pthread_mutex_init (&Mutexl, &MutexAttr) ; if((Rt = fork()) == 0){

// Сыновний процесс.

pthread_mutex_lock(&Mutexl);

// Критический раздел.

pthread_mutex_unlock(&Mutexl) ;

}

else{

// Родительский процесс,

pthread_mutex_lock(&Mutexl); // Критический раздел. pthread_mutex_unlock(&Mutexl) ;

}

//.. .

return(0);

}

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

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

pthread_mutexattr_setpshared(&MutexAttr,PTHREAD_PROCESS_SHARED);

Установка этого атрибута равным значению PTHREAD_PROCESS_SHARED позволяет объекту Mutex стать разделяемым между потоками различных процессов. После вызова функции fork () сыновний и родительский процессы могут защищать свои критические разделы с помощью объекта Mutex. Критические разделы этих процессов могут содержать некоторые ресурсы, разделяемые обоими процессами.

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

Мьютексные семафоры позволяют управлять критическими разделами, обеспечивая последовательный вход в эти разделы. В любой момент времени вход в критический раздел разрешается только одному потоку или процессу. Реализуя блокировки для чтения и записи, можно разрешить вход в критический раздел сразу нескольким потокам, если они намерены лишь считывать данные из разделяемой памяти. Следовательно, блокировкой для чтения может владеть любое количество потоков. Но если сразу несколько потоков должны записывать или модифицировать данные общей памяти, то доступ для этого будет предоставлен только одному потоку. Другими словами, никаким другим потокам не будет разрешено входить в критический раздел, если одному потоку предоставлен монопольный доступ для записи в разделяемую память. Такой подход может оказаться полезным, если приложения чаще считывают данные, чем записывают их. Если в приложении создается множество потоков, организация взаимно исключающего доступа может оказаться излишней предосторож н остью. Производительность такого приложения может значительно увеличиться, если в не м разрешить одновре м енное считывание данных нескольки м и потока м и. Стандарт 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 может быть закрытым (для разделения между потоками одного процесса) или разделяемым (для разде л ения между потоками различных процессов).

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

Операции

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

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

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

• 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

• int pthread_rwlockattr_init (pthread_rwlockattr_t * attr);  Инициализирует атрибутный объект блокировки чтения-записи,заданный параметром attr , значениями, действующими по умолчанию для всех атрибутов, определенных реализацией

• int pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr) Разрушает атрибутный объект б л окировки чтения-записи,заданный параметром attr Его можно инициализировать повторно, вызвав функцию pthread_rwlockattr_init ()

• int pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, int pshared); int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * restrict attr, int *restrict pshared); Устанавливает или возвращает атрибут process-shared атрибутного объекта блокировки чтения-записи, заданного параметром attr . Параметр pshared может содержать следующие значения:

PTHREAD_PROCESS_SHARED (разрешает б л окировку чтения-записи, разделяемую любыми потоками, которые имеют доступ к памяти, выделенной для этого объекта блокировки, даже если потоки принадлежат различным процессам);

PTHREAD_PROCESS_PRIVATE (блокировка чтения-записи разделяется между потоками одного процесса)

Использование блокировок чтения-записи для реализации стратегии доступа

Блокировки чтения-записи можно использовать для реализации стратегии доступа CREW (параллельное чтение и исключающая запись). Согласно этой стратегии возможность параллельно считывать данные может быть предоставлена сразу нескольким задачам, но только одна задача получит право доступа для записи. При выполнении монопольной записи в этом случае не будет дано разрешение на параллельное чтение данных. Использование блокировок чтения-записи для защиты критических разделов продемонстрировано в листинге 5.3.

// Листинг 5.3. Пример использования потоками блокировок

// чтения-записи

//...

pthread_t ThreadA, ThreadB, ThreadC, ThreadD ; pthread_rwlock_t RWLock;

void *producerl(void *X) {

pthread_rwlock_wrlock(&RWLock) ; // Критический раэдел.

pthread_rwlock_unlock(&RWLock) ; return(0);

56
{"b":"233145","o":1}