Главная страница  Взаимодействие нетривиальных процессов 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [ 64 ] 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

тите внимание, что мы перестаем устанавливать блокировку для считывающих потоков, если появляются потоки, ожидающие возможности записи. В противном случае постоянно появляющиеся потоки с запросами на чтение могли бы заставить поток, ожидающий возможности записи, ждать целую вечность. По этой причине мы используем два отдельных оператора 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):



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [ 64 ] 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.