Ада-95. Компилятор GNAT
66ac8edd

Селекция принятия рандеву


Предположим, что нам необходима задача-сервер, которая будет обслуживать множество задач-клиентов.

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

Вполне резонно на основе полученных ранее сведений, попытаться реализовать что-нибудь подобное следующему:

task Server_Task is

entry Service_1 [ параметры для Service_1 ] ; entry Service_2 [ параметры для Service_2 ] ; . . . entry Service_N [ параметры для Service_N ] ;

entry Stop; end task;

task body Server_Task is

. . . begin

loop

accept Service_1 [ параметры для Service_1 ] do

. . . end Service_1;

accept Service_2 [ параметры для Service_2 ] do



. . . end Service_2;

. . .

accept Service_N [ параметры для Service_N ] do

. . . end Service_N;

accept Stop do

exit ; -- выход из цикла, и, следовательно, -- завершение задачи end Stop; end loop; end Server_Task;

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

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

Следовательно, для ситуаций, которые подобны показанной в этом примере, необходима возможность селекции (или выбора) принятия рандеву на входах задачи-сервера.

Для обеспечения селекции принятия рандеву, Ада предоставляет различные варианты инструкции отбора с ожиданием, которая задается с помощью зарезервированного слова select.

Использование инструкции отбора в теле задачи-сервера позволяет:

одновременно ожидать более одного рандеву

  • выполнять таймаут, если за указанный период времени не поступило ни одного вызова рандеву




  • осуществлять рандеву только в случае наличия вызова рандеву


  • завершать выполнение задачи при отсутствии задач-клиентов, которые потенциально могут вызвать рандеву

    Рассмотрим следующий пример использования инструкции отбора в теле задачи-сервера:

    task Server_Task is

    entry Service_1 [ параметры для Service_1 ] ; entry Service_2 [ параметры для Service_2 ] ; . . . entry Service_N [ параметры для Service_N ] ;

    entry Stop; end task;

    task body Server_Task is

    . . . begin

    loop

    select

    accept Service_1 [ параметры для Service_1 ] do

    . . . end Service_1; . . . -- дополнительная последовательность инструкций, -- которая выполняется после принятия рандеву -- на входе Service_1 or

    accept Service_2 [ параметры для Service_2 ] do

    . . . end Service_2; or

    . . . or

    accept Service_N [ параметры для Service_N ] do

    . . . end Service_N; or

    accept Stop; exit ; -- выход из цикла, и, следовательно, -- завершение задачи end select

    end loop; end Server_Task;

    Как видно из исходного текста примера, инструкции принятия рандеву указываются как альтернативы выбора инструкции отбора (это несколько подобно инструкции case).

    В данном случае при выполнении инструкции отбора задача-сервер циклически "опрашивает" свои входы на наличие вызова рандеву от задач-клиентов (без блокировки в состоянии ожидания вызова рандеву на каком-либо входе).

    Опрос продолжается до тех пор, пока не будет обнаружен вызов рандеву на каком-либо входе, который соответствует одной из перечисленных инструкций принятия рандеву.

    После обнаружения вызова выполняется соответствующая альтернатива (примечание: завершение обработки альтернативы приводит к завершению инструкции отбора).

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

    Другими словами, когда инструкция отбора помещена в тело цикла, при одновременном появлении двух и более вызовов рандеву, инструкция отбора будет осуществлять обработку только одного вызова рандеву в течение одного "витка" цикла, а одновременно поступившие вызовы рандеву будут поставлены в очередь порядок которой не определяется стандартом.



    В данном примере интересную ситуацию представляет альтернатива принятия рандеву на входе Stop.

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

    Если в процессе завершения работы задачи-сервера поступит вызов рандеву на какой-либо из входов Service_1 - Service_N, то задача-клиент, вызывающая рандеву, скорее всего получит исключение Tasking_Error.

    Недостаток такого подхода состоит в том, что завершение работы задачи-сервера требует явного указания вызова рандеву на входе Stop.

    Чтобы избавиться от необходимости явного указания вызова рандеву на входе Stop, можно использовать инструкцию отбора, в которой указывается альтернатива завершения задачи:

    task Server_Task is

    entry Service_1 [ параметры для Service_1 ] ; . . . entry Service_N [ параметры для Service_N ] ; end task;

    task body Server_Task is

    . . . begin

    loop

    select

    accept Service_1 [ параметры для Service_1 ] do

    . . . end Service_1; or

    . . . or

    accept Service_N [ параметры для Service_N ] do

    . . . end Service_N; or

    terminate; -- завершение работы задачи end select

    end loop; end Server_Task;

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

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

    Следует также учитывать, что альтернатива завершения работы задачи должна указываться последней в списке альтернатив инструкции отбора.

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

    Для этого можно использовать инструкцию отбора, в которой вместо альтернативы завершения работы используется раздел else.

    Использование такого варианта инструкции отбора может иметь следующий вид:



    loop

    select

    accept Service_1 [ параметры для Service_1 ] do

    . . . end Service_1; or

    . . . or

    accept Service_N [ параметры для Service_N ] do

    . . . end Service_N; else

    . . . -- последовательность инструкций, которая выполняется -- если нет ни одного вызова рандеву end select

    end loop;

    Подобным образом последовательность инструкций, указанная в разделе else, может быть использована для организации "фоновой" работы задачи-сервера.

    Еще одной разновидностью инструкции отбора служит инструкция отбора, использующая альтернативу таймаута (или задержки).

    Такая инструкция отбора может иметь следующий вид:

    loop

    select

    accept Service_1 [ параметры для Service_1 ] do

    . . . end Service_1; or

    . . . or

    accept Service_N [ параметры для Service_N ] do

    . . . end Service_N; or

    delay 1.0; . . . -- последовательность инструкций таймаута, -- которая выполняется в случае -- когда нет ни одного вызова рандеву -- в течение одной секунды end select

    end loop;

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

    Альтернатива таймаута, указанная в инструкций отбора, позволяет задаче-серверу выполнять определенную последовательность действий, если в течение указанного интервала времени не поступило ни одного вызова рандеву.

    Интервал времени указывается в альтернативах таймаута аналогично интервалу времени в инструкциях задержки выполнения (необходимо однако заметить, что несмотря на внешнее подобие, не следует путать альтернативы таймаута инструкций отбора с инструкциями задержки выполнения).

    Таким образом, может быть указан относительный (как в примере выше) или абсолютный интервал времени.

    Если выражение, указывающее относительный интервал времени, имеет отрицательное или нулевое значение или значение абсолютного интервала времени оценивается как прошедшее, то альтернатива таймаута инструкции отбора может быть расценена как эквивалент раздела else.



    В одной инструкции отбора, допускается наличие более одной альтернативы таймаута.

    При этом, будет обрабатываться та альтернатива таймаута, которая имеет наименьший интервал времени.

    Отметим также, что в пределах одной инструкции отбора не допускается совместное использование различных альтернатив таймаута для которых одновременно заданы относительные и абсолютные интервалы времени.

    При применении инструкций отбора следует учитывать, что использование альтернативы завершения работы задачи (terminate), альтернативы таймаута (delay) и раздела else

    в пределах одной инструкции отбора является взаимно исключающим.

    Кроме того, любой вариант инструкции отбора обязан содержать хотя бы одну альтернативу выбора, в которой указана инструкция принятия рандеву accept.

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

    Для указания такой проверки используется конструкция вида: "when условие =>", - где проверяемое условие описывается с помощью логического выражения, результат вычисления которого должен иметь значение предопределенного логического типа Standard.Boolean.

    Как правило, такую проверку назвают защитной или охранной (guard), а ее использование может иметь следующий вид:

    declare

    . . . Service_1_Counter : Integer; . . . Service_N_Counter : Integer; . . . begin

    . . . loop

    . . . select

    when (Service_1_Counter > 0) => accept Service_1 [ параметры для Service_1 ] do

    . . . end Service_1; or

    . . . or

    when (Service_N_Counter > 100) => accept Service_N [ параметры для Service_N ] do

    . . . end Service_N; end select

    end loop; . . . end;

    Вычисление значения логического выражения осуществляется в начале выполнения инструкции отбора.

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

    Следует также учесть, что в случае, когда проверки условий используются для всех альтернатив инструкции отбора, и результаты проверок всех условий имеют значение False, это приведет к возбуждению исключения Program_Error.

    Альтернативу отбора можно назвать открытой, когда для нее не указана дополнительная проверка условия или когда значение результата проверки условия есть True.

    В противном случае альтернативу отбора можно назвать закрытой.


    Содержание раздела