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 даже при наличии блокировок на чтение, что приведет к потере эффективности.

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

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

#include pthread.h

int pthread_cancel(pthread_t tid);

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

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

Для обработки отмены выполнения поток может установить (push) или снять (pop) обработчик-очиститель (cleanup handler):

#include pthread.h

void pthread_cleanup_push(void (*function) (void *) void *arg);

void pthread_cleanup_pop(int execute);

Эти обработчики представляют собой обычные функции, которые вызываются:

■ в случае отмены выполнения потока (другим потоком, вызвавшим pthread_ cancel);

■ в случае добровольного завершения работы (вызовом pthread_exit или выходом из начальной функции потока).

Обработчики-очистители выполняют всю необходимую работу по восстановлению значений переменных, такую как разблокирование взаимных исключений и семафоров, которые могли быть заблокированы данным потоком.

Аргумент function представляет собой адрес вызываемой функции, а arg — ее единственный аргумент. Функция pthread_cleanup_pop всегда удаляет обработчик из верхушки стека и вызывает эту функцию, если значение execute отлично от 0.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже