Главная страница Взаимодействие нетривиальных процессов тите внимание, что мы перестаем устанавливать блокировку для считывающих потоков, если появляются потоки, ожидающие возможности записи. В противном случае постоянно появляющиеся потоки с запросами на чтение могли бы заставить поток, ожидающий возможности записи, ждать целую вечность. По этой причине мы используем два отдельных оператора i f и не можем написать просто: /* предпочтение отдается записывающим процессам */ if (rw->rw nwaitreaders > О && 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, мы обратили внимание на наличие проблемы, возникающей при отмене выполнения потока, заблокированного вызовом pthreadcondwait. Выполнение потока может быть отменено в том случае, если какой-нибудь другой поток вызовет функцию pthreadcancel, единственным аргументом которой является идентификатор потока, выполнение которого должно быть отменено: linclude <pthread.h> int pthread cancel(pthread t tid): /* Возвращает 0 в случае успешного завершения, положительное значение Еххх -в случае ошибки */ Отмена выполнения может быть использована в том случае, если несколько потоков начинают работу над какой-то задачей (например, поиск записи в базе данных) и один из них завершает работу раньше всех остальных. Тогда он может отменить их выполнение. Другим примером является обнаружение ошибки одним из одновременно выполняющих задачу потоков, который затем может отменить выполнение и всех остальных. Для обработки отмены выполнения поток может установить (push) или снять (pop) обработчик-очиститель (cleanup handler): linclude <pthread.h> void pthread cleanup push(void (*funct7on)(void *). void *arg): void pthread cleanup pop(int execute): Эти обработчики представляют собой обычные функции, которые вызываются: ш в случае отмены выполнения потока (другим потоком, вызвавшим pthread cancel); ш в случае добровольного завершения работы (вызовом pthreadexit или выходом из начальной функции потока). Обработчики-очистители вьшолняют всю необходимую работу по восстановлению значений переменных, такую как разблокирование взаимных исключений и семафоров, которые могли быть заблокированы данным потоком. Аргумент function представляет собой адрес вызываемой функции, а arg - ее единственный аргумент. Функция pthread cl eanup pop всегда удаляет обработчик из верхушки стека и вызывает эту функцию, если значение execute отлично отО. ПРИМЕЧАНИЕ - Мы снова встретимся с проблемой отмены выполнения потоков в связи с листингом 15.26, где может произойти отмена выполнения сервера с дверьми при завершении работы клиента в процессе обработки вызванной им процедуры. Пример Легче всего продемонстрировать проблему нашей реализации из предыдущего раздела с помощью примера. На рис. 8.1 изображена временная диаграмма выполнения нашей программы, а текст самой программы приведен в листинге 8.9. главный поток pthread create -sleep (1) pthread create pthread Join поток 1 - получение блокировки на чтение sleep (3) 3 возвращается pthread Join возвращается exit release lock возвращается поток 2 попытка получения блокировки - на запись pthread cancel..................► отменен sleep (3) Рис. 8.1. Временная диаграмма выполнения программы из листинга 8.9 Создание двух потоков 10-13 Создаются два потока, первый из которых выполняет функцию threadl, а второй - threadZ. После создания первого делается пауза длительностью в одну секунду, чтобы он успел заблокировать ресурс на чтение. Ожидание завершения потоков 14-23 Мы ожидаем завершения работы второго потока и проверяем, что его статус имеет значение PTHREAD CANCEL. Затем мы ждем завершения работы первого потока и проверяем, что его статус представляет собой нулевой указатель. Затем мы выводим значение трех счетчиков в структуре pthread rwl ock t и уничтожаем блокировку. Листинг 8.9. Тестовая программа, иллюстрирующая отмену выполнения потока my rwlock cancel/testcancel.с 1 linclude unpipc.h 2 linclude pthread rwlock.h 3 pthread rwlock t rwlock = PTHREAO RWLOCK INITIALIZER: 4 pthread t tidl. tid2; 5 void *threadl(void *). *thread2(void *): 6 int 7 roain(int argc. char **argv) 9 void *status: 10 Set concurrency(2): 11 Pthread create(&tidl, NULL, threadl, NULL); 12 sleep(l): /* даем первому потоку возможность получить блокировку */ 13 Pthread create(&tid2, NULL, thread2, NULL): 14 PthreadJoin(tid2, &status): 15 if (status !- PTHREAO CANCELEO) 16 printf( thread2 status = *p\n , status); 17 PthreadJoin(tidl, &status): 18 if (status != NULL) 19 printf( threadl status = *p\n , status): 20 printf( rw refcount = %й. rw nwaitreaders = %й. rw nwaitwriters = %й\г) . 21 rwlock.rw refcount, rwlock.rw nwaitreaders, 22 rwlock.rw nwaitwriters): 23 Pthread rwlock destroy(&rwiock); 24 exit(O): 25 } 26 void * 27 threadKvoid *arg) 28 { 29 Pthread rwlock rdlock(&rwlock): 30 printf( threadl() got a read lock\n ): 31 sleep(3): /* даем второму потоку возможность заблокироваться при вызове pthread rwlock wrlock() */ 32 pthread cancel(tid2); 33 sleep(3): 34 Pthread rwlock umock(&rwlock): 35 return(NULL):
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |