- •Часть 4. Локальное взаимодействие процессов
- •Глава 16. Блокирование записей 89
- •12.2. Процессы, потоки и общий доступ к информации
- •12.3. Живучесть объектов ipc
- •12.4. Пространства имен
- •12.5. Действие команд fork, exec и exit на объекты ipc
- •12.6. Комментарии к примерам ipc
- •12.7. Выводы по главе 12
- •12.8. Упражнения по главе 12
- •Глава 13. Именованные и неименованные каналы
- •13.1. Введение
- •13.2. Приложение типа клиент-сервер
- •13.3. Программные каналы
- •13.4. Функции popen и pclose
- •13.5. Именованные каналы (fifo)
- •13.6. Некоторые свойства именованных и неименованных каналов
- •13.7. Один сервер, несколько клиентов
- •13.8. Последовательные и параллельные серверы
- •13.9. Ограничения программных каналов и fifo
- •13.10. Выводы по главе 13
- •13.11. Упражнения по главе 13
- •Глава 14. Программные потоки
- •14.1. Введение
- •14.2. Концепция потоков
- •14.3. Идентификация потоков
- •14.4. Создание потока
- •14.5. Завершение потока
- •Функции управления процессами и потоками
- •14.6. Установка атрибутов потока
- •14.7. Реентерабельность
- •Альтернативные версии функций, безопасные в многопоточной среде
- •14.8. Локальные данные потоков
- •14.9. Принудительное завершение потоков
- •Некоторые точки выхода, определенные стандартом Posix.1
- •14.10. Потоки и сигналы
- •14.11. Выводы по главе 14
- •14.12. Упражнения по главе 14 Глава 15. Средства синхронизации потоков
- •15.1. Введение
- •15.2. Взаимные исключения: установка и снятие блокировки
- •15.2.1. Схема производитель-потребитель
- •15.2.2. Блокирование и опрос
- •15.2.3. Предотвращение тупиковых ситуаций
- •15.3. Условные переменные
- •15.3.1. Ожидание и сигнализация
- •15.3.2. Исключение состояния гонок
- •15.4. Блокировки чтения-записи
- •15.5. Атрибуты средств синхронизации потоков
- •15.5.1. Атрибуты взаимных исключений
- •Поведение взаимных исключений различных типов
- •15.5.2. Атрибуты условных переменных
- •15.5.3. Атрибуты блокировок чтения-записи
- •15.6. Выводы по главе 15
- •15.7. Упражнения по главе 15
- •Глава 16. Блокирование записей
- •16.1. Введение
- •16.2. Блокирование записей и файлов
- •16.3. Блокирование записей с помощью fcntl по стандарту Posix
- •16.4. Рекомендательная блокировка
- •16.5. Обязательная блокировка
- •16.6. Приоритет чтения и записи Выводы по главе 16
- •Упражнения по главе 16 Глава 17. System V ipc
- •17.1. Введение
- •17.2. Ключи типа key_t и функция ftok
- •17.3. Структура ipc_perm
- •17.4. Создание и открытие каналов ipc
- •17.5. Разрешения ipc
- •17.6. Программы ipcs и ipcrm
- •17.7. Ограничения ядра
- •17.8. Выводы по главе 17
- •17.9. Упражнения по главе 17
- •Глава 18. Очереди сообщений System V
- •18.1. Введение
- •18.2. Функция msgget
- •18.3. Функция msgsnd
- •18.4. Функция msgrcv
- •18.5. Функция msgctl
- •18.6. Пример программы клиент-сервер
- •18.7. Мультиплексирование сообщений
- •18.7.1. Пример: одна очередь на приложение
- •18.7.2. Пример: одна очередь для каждого клиента
- •18.8. Ограничения, накладываемые на очереди сообщений
- •18.9. Выводы по главе 18
- •18.10. Упражнения по главе 18
- •Глава 19. Семафоры System V
- •19.1. Введение
- •19.2. Функция semget
- •19.3. Функция semop
- •19.4. Функция semctl
- •19. . Ограничения семафоров System V
- •19. . Выводы по главе 19
- •19. . Упражнения по главе 19 Глава 20. Введение в разделяемую память
- •20.1. Введение
- •20.2. Функции mmap, munmap и msync
- •20.3. Увеличение счетчика в отображаемом в память файле
- •20.4. Неименованное отображение в память
- •20.5. Обращение к объектам, отображенным в память
- •20.6. Выводы по главе 20
- •20.7. Упражнения по главе 20
- •Глава 21. Разделяемая память System V
- •21.1. Введение
- •21.2. Функция shmget
- •21.3. Функция shmat
- •21.4. Функция shmdt
- •21.5. Функция shmctl
- •21.6. Ограничения, накладываемые на разделяемую память
- •21.7. Выводы по главе 21
- •21.8. Упражнения по главе 21
15.2.2. Блокирование и опрос
Продемонстрируем теперь, что взаимные исключения предназначены для блокирования, но не для ожидания. Изменим наш пример из предыдущего раздела таким образом, чтобы потребитель запускался сразу же после запуска всех производителей. Это даст возможность потребителю обрабатывать данные по мере их формирования производителями в отличие от программы в листинге 15.1, в которой потребитель не запускался до тех пор, пока все производители не завершали свою работу. Теперь нам придется синхронизировать потребителя с производителями, чтобы первый обрабатывал только данные, уже сформированные последними.
В листинге 15.2приведен текст программы. Начало кода (до объявления функцииconsume) не претерпело никаких изменений по сравнению слистингом 15.1. Изменения были внесены в код функцийconsume и main.
Листинг 15.2. Запуск потребителя сразу же после запуска производителей (функции consume и main)
void *consume(void *arg)
{
int i;
for (i = 0; i < nitems; i++)
{
for ( ; ; )
{
if ((res = pthread_mutex_lock(&shared.mutex)) != 0)
{
fprintf(stderr, "Ошибка запирания взаимного исключения: %s\n",
strerror(res));
exit(1);
}
if (i < shared.nput)
{
if ((res = pthread_mutex_unlock(&shared.mutex)) != 0)
{
fprintf(stderr, "Ошибка отпирания взаимного исключения: %s\n",
strerror(res));
exit(1);
}
break; /* элемент готов */
}
if ((res = pthread_mutex_unlock(&shared.mutex)) != 0)
{
fprintf(stderr, "Ошибка отпирания взаимного исключения: %s\n",
strerror(res));
exit(1);
}
}
if (shared.buff[i] != i)
printf("buff[%d] = %d\n", i, shared.buff[i]);
}
return(NULL);
}
int main(int argc, char *argv[])
{
int i, nthreads, count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS], tid_consume;
if (argc < 3)
{
printf("Использование: %s <число_элементов> <число_потоков>\n", argv[0]);
exit(-1);
}
nitems = min(atoi(argv[1]), MAXNITEMS);
nthreads = min(atoi(argv[2]), MAXNTHREADS);
if ((res = pthread_setconcurrency(nthreads + 1)) != 0)
{
fprintf(stderr, "Ошибка при вызове функции pthread_setconcurrency: %s\n",
strerror(res));
exit(1);
}
/* Запуск всех производителей и одного потребителя */
for (i = 0; i < nthreads; i++)
{
count[i] = 0;
if ((res = pthread_create(&tid_produce[i], NULL, produce, &count[i])) != 0)
{
fprintf(stderr, "Ошибка создания потока-производителя номер %d: %s\n", i,
strerror(res));
exit(1);
}
}
if ((res = pthread_create(&tid_consume, NULL, consume, NULL)) != 0)
{
fprintf(stderr, "Ошибка создания потока-потребителя: %s\n", strerror(res));
exit(1);
}
/* Ожидание завершения всех потоков-производителей и потребителя */
for (i = 0; i < nthreads; i++)
{
if ((res = pthread_join(tid_produce[i], NULL)) != 0)
{
fprintf(stderr, "Ошибка завершения потока-производителя номер %d: %s\n",
i, strerror(res));
exit(1);
}
printf("count[%d] = %d\n", i, count[i]);
}
if ((res = pthread_join(tid_consume, NULL)) != 0)
{
fprintf(stderr, "Ошибка завершения потока-потребителя: %s\n",
strerror(res));
exit(1);
}
exit(0);
}
При вызове функции pthread_setconcurrency мы увеличили количество параллельно выполняемых потоков на единицу, чтобы учесть поток-потребитель, выполняемый одновременно с производителями. Поток-потребитель создается сразу же после создания потоков-производителей. Код функции produce по сравнению с листингом 15.1 не изменился.
Единственное изменение в функции consume заключается в добавлении бесконечного цикла for(;;) для обработки очередного элемента массива. Мы должны ждать, пока производители не создадут i-й элемент. Для проверки этого условия производится запирание взаимного исключения, и значение i сравнивается с индексом производителя shared.nput. Блокировка здесь необходима, поскольку значение shared.nput может быть изменено одним из производителей в момент проверки.
Главная проблема – что делать, если нужный элемент еще не готов. Все, что нам остается, и что мы делаем в листинге 15.2, – это повторять операции в бесконечном цикле for(;;), устанавливая и снимая блокировку для проверки индекса. Это действие называется опросом (polling) и является ненужной тратой времени процессора.
Мы могли бы приостановить выполнение потока на некоторое время, но мы не знаем, на какое. Что нам действительно нужно – это использовать какое-то другое средство синхронизации, позволяющее потоку или процессу приостанавливать работу, пока не произойдет какое-либо событие.
