Синхронизация типа "производитель-потребитель"
В последнем решении раздела 2.2 для задачи поиска шаблонов в файле использованы процесс-производитель и процесс-потребитель. Производитель постоянно считывает строки ввода, определяет, какие из них содержат искомый шаблон, и передает их процессу-потребителю. Затем потребитель выводит строки, полученные от производителя. Взаимодействие между производителем и потребителем обеспечивается с помощью разделяемой переменной buffer. Метод синхронизации доступа к буферу остался неопределенным, и теперь его можно описать.
Здесь решается более простая задача типа "производитель-потребитель": копирование всех элементов массива от производителя к потребителю. Адаптация этого решения к кон-, кретной задаче из раздела 2.2 оставляется читателю (см. упражнения в конце этой главы).
Даны два процесса: Producer (производитель) и Consumer (потребитель). Процесс Producer имеет локальный массив целых чисел a [n]; Consumer — b [n]. Предполагается, что массив а инициализирован. Цель — скопировать его содержимое в массив Ь. Поскольку процессы не разделяют массивы, для их взаимодействия нужны разделяемые переменные. Пусть переменная buf — это одиночная разделяемая целочисленная переменная, которая будет служить буфером взаимодействия.
Процессы Producer и Consumer должны получать доступ к переменной buf по очереди. Сначала Producer помещает первый элемент массива а в переменную buf, затем Consumer извлекает ее, потом Producer помещает в переменную buf следующий элемент массива а и так далее. Пусть разделяемые переменные рис будут счетчиками числа помещенных и извлеченных элементов, соответственно. Их начальные значения — 0. Тогда условия синхронизации процессов Producer и Consumer могут быть записаны в следующем виде: PC: с <= р <= с+1
Значения переменных сир могут отличаться не больше, чем на 1; это значит, что Producer поместил в буфер максимум на один элемент больше, чем Consumer извлек. Код этих двух процессов приведен в листинге 2.2.
Процессы Producer и Consumer используют переменные рис (см. листинг 2.2) для синхронизации доступа к буферу buf. Операторы await применяются для приостановки процессов до тех пор, пока буфер не станет полным или пустым. Если истинно условие р == с, буфер пуст (последний помещенный в него элемент был извлечен), а если р > с — заполнен.
Если синхронизация реализуется описанным способом, говорят, что процесс находится в состоянии активного ожидания, или зациклен, поскольку он занят проверкой условия в операторе await, но все, что он делает, — это повторение цикла до тех пор, пока условие не вы-