ПРИМЕЧАНИЕ
Мы снова встретимся с проблемой отмены выполнения потоков в связи с листингом 15.26, где может произойти отмена выполнения сервера с дверьми при завершении работы клиента в процессе обработки вызванной им процедуры.
Пример
Легче всего продемонстрировать проблему нашей реализации из предыдущего раздела с помощью примера. На рис. 8.1 изображена временная диаграмма выполнения нашей программы, а текст самой программы приведен в листинге 8.9.
Рис. 8.1. Временная диаграмма выполнения программы из листинга 8.9
10-13 Создаются два потока, первый из которых выполняет функцию thread1, а второй — thread2. После создания первого делается пауза длительностью в одну секунду, чтобы он успел заблокировать ресурс на чтение.
14-23 Мы ожидаем завершения работы второго потока и проверяем, что его статус имеет значение PTHREAD_CANCEL. Затем мы ждем завершения работы первого потока и проверяем, что его статус представляет собой нулевой указатель. Затем мы выводим значение трех счетчиков в структуре pthread_rwlock_t и уничтожаем блокировку.
//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 }
26-36 Поток получает блокировку на чтение и ждет 3 секунды. Эта пауза дает возможность другому потоку вызвать pthread_rwlock_wrlock и заблокироваться при вызове pthread_cond_wait, поскольку блокировка на запись не может быть установлена из-за наличия блокировки на чтение. Затем первый поток вызывает pthread_cancel для отмены выполнения второго потока, ждет 3 секунды, освобождает блокировку на чтение и завершает работу.
37-46 Второй поток делает попытку получить блокировку на запись (которую он получить не может, поскольку первый поток получил блокировку на чтение). Оставшаяся часть функции никогда не будет выполнена.
При запуске этой программы с использованием функций из предыдущего раздела мы получим следующий результат: