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

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:

9 if ((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)

10 return(result);

11 while (rw->rw_refcount != 0) {

12 rw->rw_nwaitwriters++;

13 result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);

14 rw->rw_nwaitwriters--;

15 if (result != 0)

16 break;

17 }

18 if (result == 0)

19 rw->rw_refcount = –1;

20 pthread_mutex_unlock(&rw->rw_mutex);

21 return(result);

22 }

Функция pthread_rwlock_trywrlock

Неблокируемая

функция pthread_rwlock_trywrlock показана в листинге 8.7.

11-14 Если значение счетчика rw_refcount отлично от нуля, блокировка в данный момент уже установлена считывающим или записывающим процессом (это безразлично) и мы возвращаем ошибку с кодом EBUSY. В противном случае мы устанавливаем блокировку на запись, присвоив переменной rw_refcount значение –1.

Листинг 8.7. Функция pthread_rwlock_trywrlock: попытка получения блокировки на запись

//my_rwlock/pthread_rwlock_trywrlock.c

1 #include "unpipc.h"

2 #include "pthread_rwlock.h"

3 int

4 pthread_rwlock_trywrlock(pthread_rwlock_t *rw)

5 {

6 int result;

7 if (rw->rw_magic != RW_MAGIC)

8 return(EINVAL);

9 if ((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)

10 return(result);

11 if (rw->rw_refcount != 0)

12 result = EBUSY; /* заблокирован пишущим потоком или ожидающим возможности записи */

13 else

14 rw->rw_refcount = –1; /* доступна */

15 pthread_mutex_unlock(&rw->rw_mutex);

16 return(result);

17 }

Функция pthread_rwlock_unlock

Последняя функция, pthread_rwlock_unlock, приведена в листинге 8.8.

Листинг 8.8. Функция pthread_rwlock_unlock: разблокирование ресурса

//my_rwlock/pthread_rwlock_unlock.c

1 #include "unpipc.h"

2 #include "pthread_rwlock.h"

3 int

4 pthread_rwlock_unlock(pthread_rwlock_t *rw)

5 {

6 int result;

7 if (rw->rw_magic != RW_MAGIC)

8 return(EINVAL);

9 if ((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)

10 return(result);

11 if (rw->rw_refcount > 0)

12 rw->rw_refcount--; /* снятие блокировки на чтение */

13 else if (rw->rw_refcount == –1)

14 rw->rw_refcount = 0; /* снятие блокировки на запись */

15 else

16 err_dump("rw_refcount = %d", rw->rw_refcount);

17 /*
преимущество отдается ожидающим возможности записи потокам */

18 if (rw->rw_nwaitwriters > 0) {

19 if (rw->rw_refcount == 0)

20 result = pthread_cond_signal(&rw->rw_condwriters);

21 } else if (rw->rw_nwaitreaders > 0)

22 result = pthread_cond_broadcast(&rw->rw_condreaders);

23 pthread_mutex_unlock(&rw->rw_mutex);

24 return(result);

25 }

11-16 Если rw_refcount больше 0, считывающий поток снимает блокировку на чтение. Если rw_refcount равно –1, записывающий поток снимает блокировку на запись.

17-22 Если имеются ожидающие разрешения на запись потоки, по условной переменной rw_condwriters передается сигнал (если блокировка свободна, то есть значение счетчика rw_refcount равно 0). Мы знаем, что только один поток может осуществлять запись, поэтому используем функцию pthread_cond_signal. Если нет потоков, ожидающих возможности записи, но есть потоки, ожидающие возможности чтения, мы вызываем pthread_cond_broadcast для переменной rw_condreaders, поскольку возможно одновременное считывание несколькими потоками. Обратите внимание, что мы перестаем устанавливать блокировку для считывающих потоков, если появляются потоки, ожидающие возможности записи. В противном случае постоянно появляющиеся потоки с запросами на чтение могли бы заставить поток, ожидающий возможности записи, ждать целую вечность. По этой причине мы используем два отдельных оператора if и не можем написать просто:

/* предпочтение отдается записывающим процессам */

if (rw->rw_nwaitreaders > 0 && rw->rw_refcount == 0)

 result = pthread_cond_signal(&rw->rw_condwriters);

else if (rw->rw_nwaitreaders > 0)

 result = pthread_cond_broadcast(&rw->rw_condreaders);

Мы могли бы исключить и проверку rw->rw_refcount, но это может привести к вызовам pthread_cond_signal даже при наличии блокировок на чтение, что приведет к потере эффективности.

8.5. Отмена выполнения потоков

Обсуждая листинг 8.4, мы обратили внимание на наличие проблемы, возникающей при отмене выполнения потока, заблокированного вызовом pthread_cond_wait. Выполнение потока может быть отменено в том случае, если какой-нибудь другой поток вызовет функцию pthread_cancel, единственным аргументом которой является идентификатор потока, выполнение которого должно быть отменено:

#include <pthread.h>

int pthread_cancel(pthread_t tid);

/* Возвращает 0 в случае успешного завершения, положительное значение Еххх –в случае ошибки */

Отмена выполнения может быть использована в том случае, если несколько потоков начинают работу над какой-то задачей (например, поиск записи в базе данных) и один из них завершает работу раньше всех остальных. Тогда он может отменить их выполнение. Другим примером является обнаружение ошибки одним из одновременно выполняющих задачу потоков, который затем может отменить выполнение и всех остальных.

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