Книга: UNIX: взаимодействие процессов
7.4. Блокировка и ожидание
Разделы на этой странице:
7.4. Блокировка и ожидание
Продемонстрируем теперь, что взаимные исключения предназначены для блокирования, но не для ожидания. Изменим наш пример из предыдущего раздела таким образом, чтобы потребитель запускался сразу же после запуска всех производителей. Это даст возможность потребителю обрабатывать данные по мере их формирования производителями в отличие от пpoгрaммы в листинге 7.1, в которой потребитель не запускался до тех пор, пока все производители не завершали свою работу. Теперь нам придется синхронизовать потребителя с производителями, чтобы первый обрабатывал только данные, уже сформированные последними.
В листинге 7.3 приведен текст функции main. Начало кода (до объявления функции main) не претерпело никаких изменений по сравнению с листингом 7.1.
Листинг 7.3. Функция main: запуск потребителя сразу после запуска производителей
//mutex/prodcons3.c
14 int
15 main(int argc, char **argv)
16 {
17 int i, nthreads, count[MAXNTHREADS];
18 pthread_t tid_produce[MAXNTHREADS], tid_consume;
19 if (argc != 3)
20 err_quit("usage: prodcons3 <#items> <#threads>");
21 nitems = min(atoi(argv[1]), MAXNITEMS);
22 nthreads = min(atoi(argv[2]), MAXNTHREADS);
23 /* создание всех производителей и одного потребителя */
24 Set_concurrency(nthreads + 1);
25 for (i = 0; i < nthreads; i++) {
26 count[i] = 0;
27 Pthread_create(&tid_produce[i], NULL, produce, &count[i]);
28 }
29 Pthread_create(&tid_consume, NULL, consume, NULL);
30 /* ожидание завершения производителей и потребителя */
31 for (i = 0; i < nthreads; i++) {
32 Pthread_join(tid_produce[i], NULL);
33 printf("count[%d] = %dn", i, count[i]);
34 }
35 Pthread_join(tid_consume, NULL);
36 exit(0);
37 }
24 Мы увеличиваем уровень параллельного выполнения на единицу, чтобы учесть поток-потребитель, выполняемый параллельно с производителями.
25-29 Поток-потребитель создается сразу же после создания потоков-производителей.
Функция produce по сравнению с листингом 7.2 не изменяется. В листинге 7.4 приведен текст функции consume, вызывающей новую функцию consume_wait.
Листинг 7.4. Функции consume и consume_wait
//mutex/prodcons3.с
54 void
55 consume wait(int i)
56 {
57 for (;;) {
58 Pthread_mutex_lock(&shared.mutex);
59 if (i < shared.nput) {
60 Pthread_mutex_unlock(&shared.mutex);
61 return; /* элемент готов */
62 }
63 Pthread_mutex_unlock(&shared.mutex);
64 }
65 }
66 void *
67 consume(void *arg)
68 {
69 int i;
70 for (i = 0; i < nitems; i++) {
71 consume_wait(i);
72 if (shared.buff[i] != i)
73 printf("buff[%d] = %dn", i, shared.buff[i]);
74 }
75 return(NULL);
76 }
Потребитель должен ждать
71 Единственное изменение в функции consume заключается в добавлении вызова consume_wait перед обработкой следующего элемента массива.
Ожидание производителей
57-64 Наша функция consume_wait должна ждать, пока производители не создадут i-й элемент. Для проверки этого условия производится блокировка взаимного исключения и значение i сравнивается с индексом производителя nput. Блокировка необходима, поскольку значение nput может быть изменено одним из производителей в момент его проверки.
Главная проблема — что делать, если нужный элемент еще не готов. Все, что нам остается и что мы делаем в листинге 7.4, — это повторять операции в цикле, устанавливая и снимая блокировку и проверяя значение индекса. Это называется опросом (spinning или polling) и является лишней тратой времени процессора.
Мы могли бы приостановить выполнение процесса на некоторое время, но мы не знаем, на какое. Что нам действительно нужно — это использовать какое-то другое средство синхронизации, позволяющее потоку или процессу приостанавливать работу, пока не произойдет какое-либо событие.
- 7.1. Введение
- 7.2. Взаимные исключения: установка и снятие блокировки
- 7.3. Схема производитель-потребитель
- 7.4. Блокировка и ожидание
- 7.5. Условные переменные: ожидание и сигнализация
- 7.6. Условные переменные: время ожидания и широковещательная передача
- 7.7. Атрибуты взаимных исключений и условных переменных
- 7.8. Резюме
- Упражнения
- Статистика по блокировкам
- 8.6.1. Блокировка приема спама
- 8.6.2. Блокировка пересылки спама
- 14.2.2. Блокировка POSIX: fcntl() и lockf()
- Ожидание процесса
- Глава пятая. Затянувшееся ожидание
- Листинг 5.4. (sem_pv.c) Ожидание и установка двоичного семафора
- 13.2.7. Ожидание события
- 14.2. Блокировка файлов
- 14.2.4. Обязательная блокировка
- Другие средства работы со спин-блокировками
- BKL: Большая блокировка ядра