Уильям Стивенс - UNIX: взаимодействие процессов Страница 49

Тут можно читать бесплатно Уильям Стивенс - UNIX: взаимодействие процессов. Жанр: Компьютеры и Интернет / Программирование, год -. Так же Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте «WorldBooks (МирКниг)» или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Уильям Стивенс - UNIX: взаимодействие процессов

Уильям Стивенс - UNIX: взаимодействие процессов краткое содержание

Прочтите описание перед тем, как прочитать онлайн книгу «Уильям Стивенс - UNIX: взаимодействие процессов» бесплатно полную версию:
Книга написана известным экспертом по операционной системе UNIX и посвящена описанию одной из форм межпроцессного взаимодействия, IPC, с использованием которой создается большинство сложных программ. В ней описываются четыре возможности разделения решаемых задач между несколькими процессами или потоками одного процесса: передача сообщений, синхронизация, разделяемая память, удаленный вызов процедур.Книга содержит большое количество иллюстрирующих примеров и может использоваться как учебник по IPC, и как справочник для опытных программистов.

Уильям Стивенс - UNIX: взаимодействие процессов читать онлайн бесплатно

Уильям Стивенс - UNIX: взаимодействие процессов - читать книгу онлайн бесплатно, автор Уильям Стивенс

Рис. 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

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

1. Второй поток вызвал pthread_rwlock_wrlock (листинг 8.6), которая была заблокирована в вызове pthread_cond_wait.

2. Первый поток вернулся из вызова slеер(3) и вызвал pthread_cancel.

3. Второй поток был отменен и завершил работу. При отмене потока, заблокированного в ожидании сигнала по условной переменной, взаимное исключение блокируется до вызова первого обработчика-очистителя. (Мы не устанавливали обработчик, но взаимное исключение все равно блокируется до завершения потока.) Следовательно, при отмене выполнения второго потока взаимное исключение осталось заблокированным и значение rw_nwaitwriters в листинге 8.6 было увеличено.

4. Первый поток вызывает pthread_rwlock_unlock и блокируется навсегда при вызове pthread_mutex_lock (листинг 8.8), потому что взаимное исключение все еще заблокировано отмененным потоком.

Если мы уберем вызов pthread_rwlock_unlock в функции thread1, функция main выведет вот что:

rw_refcount = 1, rw_nwaitreaders = 0, rw_nwaitwriters = 1

pthread_rwlock_destroy error: Device busy 

Первый счетчик имеет значение 1, поскольку мы удалили вызов pthread_rwlock_ unlock, а последний счетчик имеет значение 1, поскольку он был увеличен вторым потоком до того, как тот был отменен.

Исправить эту проблему просто. Сначала добавим две строки к функции pthread_rwlock_rdlock в листинге 8.4. Строки отмечены знаком +:

  rw->rw_nwaitreaders++;

+ pthread_cleanup_push(rwlock_cancelrdwait, (void *) rw);

  result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);

+ pthread_cleanup_pop(0);

  rw->rw_nwaitreaders++;

Первая новая строка устанавливает обработчик-очиститель (функцию rwlock_cancelrdwait), а его единственным аргументом является указатель rw. После возвращения из pthread_cond_wait вторая новая строка удаляет обработчик. Аргумент функции pthread_cleanup_pop означает, что функцию-обработчик при этом вызывать не следует. Если этот аргумент имеет ненулевое значение, обработчик будет сначала вызван, а затем удален.

Если поток будет отменен при вызове pthread_cond_wait, возврата из нее не произойдет. Вместо этого будут запущены обработчики (после блокирования соответствующего взаимного исключения, как мы отметили в пункте 3 чуть выше).

В листинге 8.10 приведен текст функции rwlock_cancelrdwait, являющейся обработчиком-очистителем для phtread_rwlock_rdlock.

Листинг 8.10. Функция rwlock_cancelrdwait: обработчик для блокировки чтения

//my_rwlock_cancel/pthread_rwlock_rdlock.с

3  static void

4  rwlock_cancelrdwait(void *arg)

5  {

6   pthread_rwlock_t *rw;

7   rw = arg;

8   rw->rw_nwaitreaders--;

9   pthread_mutex_unlock(&rw->rw_mutex);

10 }

8-9 Счетчик rw_nwaitreaders уменьшается, а затем разблокируется взаимное исключение. Это состояние, которое должно быть восстановлено при отмене потока.

Аналогично мы исправим текст функции pthread_rwlock_wrlock из листинга 8.6. Сначала добавим две новые строки рядом с вызовом pthread_cond_wait:

  rw->rw_nwaitreaders++;

+ pthread_cleanup_push(rwlock_cancelrwrwait, (void*) rw);

  result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);

+ pthread_cleanup_pop(0);

  rw->rw_nwaitreaders--;

В листинге 8.11 приведен текст функции rwlock_cancelwrwait, являющейся обработчиком-очистителем для запроса блокировки на запись.

Листинг 8.11. Функция rwlock_cancelwrwait: обработчик для блокировки записи

//my_rwlock_cancel/pthread_rwlock_wrlock.с

3  static void

4  rwlock_cancelwrwait(void *arg)

5  {

6   pthread_rwlock_t *rw;

7   rw = arg;

8   rw->rw_nwaitwriters––;

9   pthread_mutex_unlock(&rw->rw_mutex);

10 }

8-9 Счетчик rw_nwaitwriters уменьшается, и взаимное исключение разблокируется. При запуске нашей тестовой программы из листинга 8.9 с этими новыми функциями мы получим правильные результаты:

solaris %testcancel

thread1() got a read lock

thread2() trying to obtain a write lock

rw_refcount = 0, rw_nwaitreaders = 0, rw_nwaitwriters = 0

Теперь три счетчика имеют правильные значения, первый поток возвращается из вызова pthread_rwlock_unlock, а функция pthread_rwlock_destroy не возвращает ошибку EBUSY.

ПРИМЕЧАНИЕ

Этот раздел представляет собой обзор вопросов, связанных с отменой выполнения потоков. Для более детального изучения этих проблем можно обратиться, например, к разделу 5.3 книги [3].

8.6. Резюме

Блокировки чтения-записи позволяют лучше распараллелить работу с данными, чем обычные взаимные исключения, если защищаемые данные чаще считываются, чем изменяются. Функции для работы с этими блокировками определены стандартом Unix 98, их мы и описываем в этой главе. Аналогичные или подобные им функции должны появиться в новой версии стандарта Posix. По виду функции аналогичны функциям для работы со взаимными исключениями (глава 7).

Блокировки чтения-записи легко реализовать с использованием взаимных исключений и условных переменных. Мы приводим пример возможной реализации. В нашей версии приоритет имеют записывающие потоки, но в некоторых других версиях приоритет может быть отдан и считывающим потокам.

Потоки могут быть отменены в то время, когда они находятся в заблокированном состоянии, в частности при вызове pthread_cond_wait, и на примере нашей реализации мы убедились, что при этом могут возникнуть проблемы. Решить эту проблему можно путем использования обработчиков-очистителей.

Упражнения

1. Измените реализацию в разделе 8.4 таким образом, чтобы приоритет имели считывающие, а не записывающие потоки.

Перейти на страницу:
Вы автор?
Жалоба
Все книги на сайте размещаются его пользователями. Приносим свои глубочайшие извинения, если Ваша книга была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.
Комментарии / Отзывы
    Ничего не найдено.