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

ЖАНРЫ

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

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

Шрифт:

Для обработки отмены выполнения поток может установить (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.

ПРИМЕЧАНИЕ

Мы снова встретимся с проблемой отмены выполнения потоков в связи с листингом 15.26, где может произойти отмена выполнения сервера с дверьми при завершении работы клиента в процессе обработки вызванной им процедуры. 

Пример

Легче всего продемонстрировать проблему нашей реализации из предыдущего раздела с помощью примера. На рис. 8.1 изображена временная диаграмма выполнения нашей программы, а текст самой программы приведен в листинге 8.9. 

Рис. 8.1. Временная диаграмма выполнения программы из листинга 8.9

Создание двух потоков

10-13 Создаются два потока, первый из которых выполняет функцию thread1, а второй — thread2. После создания первого делается пауза длительностью в одну секунду, чтобы он успел заблокировать ресурс на чтение. 

Ожидание завершения потоков

14-23 Мы ожидаем завершения работы второго потока и проверяем, что его статус имеет значение PTHREAD_CANCEL. Затем мы ждем завершения работы первого потока и проверяем, что его статус представляет собой нулевой указатель. Затем мы выводим значение трех счетчиков в структуре pthread_rwlock_t и уничтожаем блокировку.

Листинг 8.9. Тестовая программа, иллюстрирующая отмену выполнения потока

//my_rwlock_cancel/testcancel.с

1 #include "unpipc.h"

2 #include "pthread_rwlock.h"

3 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

4 pthread_t tid1, tid2;

5 void *thread1(void *), *thread2(void *);

6 int

7 main(int argc, char **argv)

8 {

9 void *status;

10 Set_concurrency(2);

11 Pthread_create(&tid1, NULL, thread1, NULL);

12 sleep(1); /* даем первому потоку возможность получить блокировку */

13 Pthread_create(&tid2, NULL, thread2, NULL);

14 Pthread_join(tid2, &status);

15 if (status != PTHREAD_CANCELED)

16 printf("thread2 status = %p\n", status);

17 Pthread_join(tid1, &status);

18 if (status != NULL)

19 printf("thread1 status = %p\n", status);

20 printf("rw_refcount = %d, rw_nwaitreaders = %d, rw_nwaitwriters = %d\n",

21 rwlock.rw_refcount, rwlock.rw_nwaitreaders,

22 rwlock.rw_nwaitwriters);

23 Pthread_rwlock_destroy(&rwlock);

24 exit(0);

25 }

26 void *

27 thread1(void *arg)

28 {

29 Pthread_rwlock_rdlock(&rwlock);

30 printf("thread1 got a read lock\n");

31 sleep(3); /*
даем второму потоку возможность заблокироваться при вызове pthread_rwlock_wrlock */

32 pthread_cancel(tid2);

33 sleep(3);

34 Pthread_rwlock_unlock(&rwlock);

35 return(NULL);

36 }

37 void *

38 thread2(void *arg)

39 {

40 printf("thread2 trying to obtain a write lock\n"):

41 Pthread_rwlock_wrlock(&rwlock);

42 printf("thread2 got a write lock\n"); /* не будет выполнено */

43 sleep(1);

44 Pthread_rwlock_unlock(&rwlock);

45 return(NULL);

46 }

Функция thread1

26-36 Поток получает блокировку на чтение и ждет 3 секунды. Эта пауза дает возможность другому потоку вызвать pthread_rwlock_wrlock и заблокироваться при вызове pthread_cond_wait, поскольку блокировка на запись не может быть установлена из-за наличия блокировки на чтение. Затем первый поток вызывает pthread_cancel для отмены выполнения второго потока, ждет 3 секунды, освобождает блокировку на чтение и завершает работу.

Функция thread2

37-46 Второй поток делает попытку получить блокировку на запись (которую он получить не может, поскольку первый поток получил блокировку на чтение). Оставшаяся часть функции никогда не будет выполнена.

При запуске этой программы с использованием функций из предыдущего раздела мы получим следующий результат:

solaris % testcancel

thread1 got a read lock

thread2 trying to obtain a write lock

и мы никогда не вернемся к приглашению интерпретатора. Программа зависнет. Произошло вот что:

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