Пример:

select

КОНТРОЛЛЕР.ЗАХВАТИТЬ (СРЕДНИЙ) (НЕКОТОРЫЙ_ЭЛЕМЕНТ); ог

delay 45.0;

  • - контроллер слишком занят, попробуйте что-либо еще

end select;

  1. Приоритеты

Каждая задача может (но не обязательно) иметь приоритет со значени­ем подтипа PRIORITY (типа INTEGER), описанного в предопределенном библиотечном пакете SYSTEM (см. разд. 13.7). Меньшее значение приорите­та указывает на меньшую степень важности; диапазон приоритетов опреде­ляется реализацией. Приоритет связывается с задачей в том случае, если в спецификации соответствующей задачи присутствует прагма:

pragma PRIORITY (статическое_выражение);

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

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

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

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

  1. Примечание. Приоритет задачи является статическим, и поэтому фиксирован. Од­нако приоритет во время рандеву может и не быть статическим, поскольку он также зависит от приоритета задачи, вызывающей вход. Приоритеты следует использовать только для указания относительной степени важности; их не следует использовать для синхронизации задач.Атрибуты задач и входов

Для задачного объекта или значения Т определены следующие атрибуты: T'CALLABLE Вырабатывает значение FALSE, если выполнение указанной Т задачи либо закончено, либо завершено, либо задача аварийная. В остальных случаях вырабатывает значение TRUE. Значение этого атрибута имеет пред­определенный тип BOOLEAN.

T'TERMINATED Вырабатывает значение TRUE, если указанная Т задача за­вершена. В остальных случаях вырабатывает значение FALSE. Значение это­го атрибута имеет предопределенный тип BOOLEAN.

В дополнение к приведенным для задачного объекта Т или задачного типа Т определены атрибуты представления STORAGE .SIZE. SIZE и ADDRESS (см. 13.7.2).

Атрибут COUNT определен для входа Е задачного модуля Т. Вход мо­жет быть либо одиночным входом, либо входом семейства (в любом случае имя одиночного входа или семейства входов может быть либо простым, ли­бо расширенным). Этот атрибут допустим только в теле Т, но не во вложен­ном в тело Т программном модуле.

E'COUNT Вырабатывает число вызовов входа, присутствующие в очере­ди входа Е в данный момент (если атрибут вычисляется при выполнении оператора принятия входа Е, то в это число не включается вызывающая за­дача) . Значение атрибута имеет тип универсальный_целый.

Примечание. Алгоритмы, соответствующие программы которых используют атри­бут E'COUNT, обязаны учитывать возможность увеличения значения атрибута с появ­лением новых вызовов и уменьшения этого значения, например, при временных вызо­вах входа.

  1. Операторы прекращения

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

оператор_прекращения : : = abort нмя_задачи {, имя_задачм};

При определении типа имени каждой задачи используется тот факт, что это задачный тип.

При выполнении оператора прекращения заданные имена задач вычис­ляются в порядке, который в языке не определен. Затем каждая упомяну­тая задача становится аварийной, если она еще не завершена; аналогично, любая зависящая от упомянутой задача становится также аварийной, если она еще не завершена.

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

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

Вызов входа аварийной задачи возбуждает в месте вызова исключение TASKING—ERROR. Аналогично, исключение TASKING_ERROR возбужда­ется в любой задаче, вызвавшей вход аварийной задачи, если вызов входа все еще находится в очереди, либо рандеву не окончено (вызовом входа может быть либо оператор вызова входа, либо операторы условного или временного вызова входа); исключение возбуждается не позже оконча­ния аварийной задачи. Для любой аварийной (или законченной) задачи значение атрибута CALLABLE есть FALSE.

Если аварийное окончание задачи произошло во время изменения в задаче некоторой переменной, то значение этой переменной неопределено.

Пример:

abort ПОЛЬЗОВАТЕЛЬ, ТЕРМИНАЛ, all, ПУЛ (3);

Примечание. Оператор прекращения следует использовать только в крайних слу­чаях, требующих безусловного завершения. Допускается, что задача может прекратить любую задачу, включая себя.

  1. Разделяемые переменные

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

Если две задачи считывают или изменяют разделяемую переменную (т. е. доступную обеим задачам переменную), то ни одна из них ничего не может знать о порядке выполнения этих операций над переменной, исклю­чая точки синхронизации. Две задачи синхронизируются в начале и в конце их рандеву. В начале и в конце своей активизации задача синхронизируется с вызвавшей эту активизацию задачей. Задача, которая закончила свое вы­полнение, синхронизована с любой другой задачей.

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

  • Если в интервале времени между двумя точками синхронизации зада­ча считывает разделяемую переменную скалярного или ссылочного типа, то эта переменная не изменяется никакой другой задачей в течение данного ин­тервала времени.

  • Если в интервале времени между двумя точками синхронизации зада­ча изменяет разделяемую переменную скалярного или ссылочного типа, то эта переменная не считывается и не изменяется никакой другой задачей в течение данного интервала времени.

Выполнение программы ошибочно, если какое-либо из этих предполо­жений нарушено.

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

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

Для задания того, что каждое считывание или изменение значения раз­деляемой переменной является для этой переменной точкой синхронизации, может быть использована прагма SHARED, т. е. для данной переменной (но не обязательно для остальных) сделанные выше предположения справедли­вы. Форма этой прагмы следующая:

pragma SHARED (простоетля_персменной);

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

Реализация должна ограничивать объекты, для которых допустима праг­ма SHARED, объектами, для которых каждое прямое считывание или1 пря­мое изменение реализуется неделимыми операциями типа.

  1. Пример использования задачи

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

loop

  • - выработка следующего символа СИМВ

БУФЕР. ПИСАТЬ (СИМВ);

exit when СИМВ = ASC1I.EOT;

end loop;

потребляющая задача операторы:

loop

БУФЕР. ЧИТАТЬ (СИМВ);

  • - использование символа СИМВ

exit when СИМВ = ASCII.EOT;

end loop;

Задача буферизации содержит внутренний пул для символов, обрабаты­ваемых циклически. Пул имеет два индекса: ВХ_ИНДЕКС, указывающий место следующего вводимого символа, и ВЫХ_ИНДЕКС — место следующе­го выводимого символа.

task БУФЕР is

entry ЧИТАТЬ (С: out CHARACTER);

entry ПИСАТЬ (C: in CHARACTER);

end

task body БУФЕР is

РАЗМЕР-ПУЛА: constant INTEGER: = 100;

ПУЛ: array (1. .РАЗМЕР_ПУЛА) of CHARACTER;

СЧЕТЧИК: INTEGER range 0. .РАЗМЕР_ПУЛА: = 0;

ВХ-ИНДЕКС, ВЫХ_ИНДЕКС: INTEGER range 0. ,РАЗМЕР_ПУЛА: = 1;

begin

loop

select

when СЧЕТЧИК < РАЗМЕР-ПУЛА = >

accept ПИСАТЬ (C: in CHARACTER) do

ПУЛ (ВХ_ИНДЕКС): = C;

end;

ВХ_ИНДЕКС: = ВХ_ИНДЕКС mod РАЗМЕР_ПУЛА + 1;

СЧЕТЧИК: = СЧЕТЧИК + 1;

or when СЧЕТЧИК > 0 =>

accept ЧИТАТЬ (C: out CHARACTER) do

С: = ПУЛ (ВЫХ-ИНДЕКС);

end;

ВЫХ_ИНДЕКС: = ВЫХ_ИНДЕКС mod РАЗМЕР_ПУЛА + 1;

СЧЕТЧИК: = СЧЕТЧИК -1; or terminate;

end select;

end loop;

end БУФЕР;

  1. СТРУКТУРА ПРОГРАММЫ И РЕЗУЛЬТАТ КОМПИЛЯЦИИ

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

  1. Компилируемые модули. Библиотечные модули

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

компиляция : : = {компилируемый_модуль} компилируемый_модуль : : =

спецификатор—контекста библиотечный_модуль

I спецификатор_контекста вторичный_модуль библиотечный__модуль : : = описание_подпрограммы

I описание_пакета | описание_настройки

I конкретизация_настройки | тело_подпрограммы вторичный__модуль : : =

тело__библиотечного_модуля I субмодуль тело_библиотечного_модуля : : =

тело„подпрограммы | тело_пакета

Говорят, что компилируемые модули программы принадлежат програм­мной библиотеке. Компилируемый модуль определяет библиотечный мо­дуль или вторичный модуль. Вторичный модуль — это раздельно компили­руемое соответствующее тело библиотечного модуля или субмодуль друго­го компилируемого модуля. Обозначением раздельно компилируемой под­программы (библиотечного модуля или субмодуля) должен быть иденти­фикатор. В программной библиотеке простые имена всех библиотечных мо­дулей должны быть различными идентификаторами.

Результат компилирования библиотечного модуля состоит в том, чтобы определить (или переопределить) его как модуль программной библиоте­ки. По правилам видимости каждый библиотечный модуль рассматривается как описание, приведенное непосредственно внутри пакета STANDARD.

Результат компилирования вторичного модуля состоит в том, чтобы оп­ределить тело библиотечного модуля или, в случае субмодуля, определить соответствующее тело программного модуля, описанного внутри другого компилируемого модуля.