Операционные системы. Управление ресурсами




. Примитивы синхронизации в языках программирования - часть 2


Как мы уже отмечали выше, производитель и потребитель работают с разными секциями буфера, и взаимное исключение для них не требуется. Процессы - производитель и потребитель - могут перекрываться в любых своих фазах, кроме операций advance (строки 15 и 31). Переменные inCnt и outCnt являются счетчиками событий - производства порции и потребления порции соответственно. Кроме того, каждый процесс хранит в собственной локальной переменной portNum - номер порции, с которой ему предстоит работать (счет начинается с 1). Потребитель ждет, пока счетчик производств не достигнет номера очередной его порции, затем выбирает порцию из буфера и увеличивает счетчик потреблений. Производитель работает симметрично. Обратите внимание на второй параметр операции await в производителе (строка 27). Он задается таким, чтобы обеспечить отсутствие ожидания при наличии хотя бы одной свободной секции в буфере.

Другой механизм синхронизации носит название секвенсоров (sequencer). Буквальный перевод этого слова - "упорядочиватель" - так называются средства, которые выстраивают неупорядоченные события в определенном порядке. Как и счетчик событий, секвенсор представляется целым числом, над которым выполняется единственная операция: ticket. Операция ticket(S) возвращает текущее значение секвенсора и увеличивает его на 1. Операция является атомарной. Начальное значение секвенсора - 0.

Имея в своем распоряжении секвенсоры мы можем так записать решение задачи производителей-потребителей для произвольного числа процессов:

1 /* типы данных - счетчик событий и секвенсор */ 2 typedef unsigned int eventcounter; 3 typedef unsigned int sequencer; 4 static eventcounter inCnt = 0, outCnt = 0; /* счетчики для чтения и записи */ 5 /* секвенсоры для чтения и записи */ 6 static sequencer inSeq = 0, outSeq = 0; 7 static portion buffer [BUFSIZE]; /* буфер */ 8 /*== процесс-производитель ==*/ 9 void producer ( void ) { 10 int portNum; /* номер порции */ 11 portion work; /* рабочая область для порции */ 12 /* цикл производства */ 13 while (1) { 14 < производство порции в work > 15 /* получение "билета" на запись порции */ 16 portNum = ticket (inSeq); 17 /* ожидание номера порции */ 18 await (inCnt, portNum); 19 /* ожидание свободного места в буфере */ 20 await (outCnt, portNum - BSIZE+1); 21 /* запись в буфер */ 22 memcpy (buffer + portNum % BSIZE, &work, sizeof(portion) ); 23 /* продвижение счетчика чтения */ 24 advance (inCnt); 25 } 26 } 27 /*= процесс-потребитель =*/ 28 void consumer ( void ) { 29 int portNum; /* номер порции */ 30 portion work; /* рабочая область для порции */ 31 /* цикл потребления */ 32 while (1) { 33 /* получение "билета" на выборку порции */ 34 portNum = ticket (outSeq); 35 /* ожидание номера порции */ 36 await (outCnt, portNum); 37 /* ожидание появления в буфере */ 38 await (inCnt, portNum+1); 39 /* выборка порции */ 40 memcpy (&work, buffer + portNum % BSIZE, sizeof(portion) ); 41 /* продвижение счетчика записи */ 42 advance (outCnt); 43 < обработка порции в work > 44 } 45 }




Содержание  Назад  Вперед