Пример:
select
КОНТРОЛЛЕР.ЗАХВАТИТЬ (СРЕДНИЙ) (НЕКОТОРЫЙ_ЭЛЕМЕНТ); ог
delay 45.0;
- контроллер слишком занят, попробуйте что-либо еще
end select;
Приоритеты
Каждая задача может (но не обязательно) иметь приоритет со значением подтипа PRIORITY (типа INTEGER), описанного в предопределенном библиотечном пакете SYSTEM (см. разд. 13.7). Меньшее значение приоритета указывает на меньшую степень важности; диапазон приоритетов определяется реализацией. Приоритет связывается с задачей в том случае, если в спецификации соответствующей задачи присутствует прагма:
pragma PRIORITY (статическое_выражение);
Приоритет задается значением выражения. Если такая прагма присутствует в самом внешнем разделе описаний главной программы, то приоритет связывается с главной программой. В спецификации данной задачи или для подпрограммы — библиотечного модуля может употребляться не более одной такой прагмы, и это единственные места, допустимые для этой прагмы. Прагма PRIORITY игнорируется при ее появлении в подпрограмме, не являющейся главной программой.
Спецификация приоритета является указанием, помогающим реализации в распределении ресурсов между параллельными задачами, когда число выполняемых задач превышает возможности их одновременной обработки имеющимися ресурсами. Влияние приоритетов на порядок очередности выполнения задач определяется следующим правилом:
если две задачи с разными приоритетами готовы к выполнению и могут практически выполняться, используя одни и те же физические процессоры и одни и те же ресурсы обработки, то нельзя, чтобы выполнялась задача с более низким приоритетом, а не выполнялась задача с более высоким приоритетом.
Для задач с одинаковыми приоритетами порядок выполнения в языке не определен. Для задач, приоритеты которых не заданы, правила очередности не определены, исключая случай, когда между задачами происходит рандеву. Если приоритеты обеих задач определены, то рандеву выполняется с той задачей, лей приоритет является наибольшим. Если приоритет определен только для одной из двух задач, то рандеву выполняется как минимум с приоритетной задачей. Если приоритеты задач не заданы, то приоритет рандеву также не определен.
Примечание. Приоритет задачи является статическим, и поэтому фиксирован. Однако приоритет во время рандеву может и не быть статическим, поскольку он также зависит от приоритета задачи, вызывающей вход. Приоритеты следует использовать только для указания относительной степени важности; их не следует использовать для синхронизации задач.Атрибуты задач и входов
Для задачного объекта или значения Т определены следующие атрибуты: T'CALLABLE Вырабатывает значение FALSE, если выполнение указанной Т задачи либо закончено, либо завершено, либо задача аварийная. В остальных случаях вырабатывает значение TRUE. Значение этого атрибута имеет предопределенный тип BOOLEAN.
T'TERMINATED Вырабатывает значение TRUE, если указанная Т задача завершена. В остальных случаях вырабатывает значение FALSE. Значение этого атрибута имеет предопределенный тип BOOLEAN.
В дополнение к приведенным для задачного объекта Т или задачного типа Т определены атрибуты представления STORAGE .SIZE. SIZE и ADDRESS (см. 13.7.2).
Атрибут COUNT определен для входа Е задачного модуля Т. Вход может быть либо одиночным входом, либо входом семейства (в любом случае имя одиночного входа или семейства входов может быть либо простым, либо расширенным). Этот атрибут допустим только в теле Т, но не во вложенном в тело Т программном модуле.
E'COUNT Вырабатывает число вызовов входа, присутствующие в очереди входа Е в данный момент (если атрибут вычисляется при выполнении оператора принятия входа Е, то в это число не включается вызывающая задача) . Значение атрибута имеет тип универсальный_целый.
Примечание. Алгоритмы, соответствующие программы которых используют атрибут E'COUNT, обязаны учитывать возможность увеличения значения атрибута с появлением новых вызовов и уменьшения этого значения, например, при временных вызовах входа.
Операторы прекращения
Оператор прекращения переводит одну из нескольких задач в аварийное состояние, предотвращая любые дальнейшие рандеву с такими задачами.
оператор_прекращения : : = abort нмя_задачи {, имя_задачм};
При определении типа имени каждой задачи используется тот факт, что это задачный тип.
При выполнении оператора прекращения заданные имена задач вычисляются в порядке, который в языке не определен. Затем каждая упомянутая задача становится аварийной, если она еще не завершена; аналогично, любая зависящая от упомянутой задача становится также аварийной, если она еще не завершена.
Любая аварийная задача, выполнение которой приостановлено операторами принятия, отбора или задержки, становится законченной. Любая аварийная задача, выполнение которой приостановлено при вызове входа, а соответствующее рандеву еще не началось, становится законченной и удаляется из очереди ко входу. Любая аварийная задача, которая не начала свою активизацию, становится законченной (и, следовательно, также и завершенной) . Этим заканчивается выполнение оператора прекращения.
Окончание любой другой аварийной задачи не производится до окончания выполнения оператора прекращения. Оно должно произойти не позже достижения аварийной задачей точки синхронизации, которой может быть конец ее активизации, начало активизации другой задачи, вызов входа, начало или конец выполнения оператора принятия, оператор отбора, оператор задержки, обработчик исключения или оператор прекращения. Если задача, вызвавшая вход, становится аварийной в ходе рандеву, то ее завершение не производится до окончания рандеву (см. разд. 11.5).
Вызов входа аварийной задачи возбуждает в месте вызова исключение TASKING—ERROR. Аналогично, исключение TASKING_ERROR возбуждается в любой задаче, вызвавшей вход аварийной задачи, если вызов входа все еще находится в очереди, либо рандеву не окончено (вызовом входа может быть либо оператор вызова входа, либо операторы условного или временного вызова входа); исключение возбуждается не позже окончания аварийной задачи. Для любой аварийной (или законченной) задачи значение атрибута CALLABLE есть FALSE.
Если аварийное окончание задачи произошло во время изменения в задаче некоторой переменной, то значение этой переменной неопределено.
Пример:
abort ПОЛЬЗОВАТЕЛЬ, ТЕРМИНАЛ, all, ПУЛ (3);
Примечание. Оператор прекращения следует использовать только в крайних случаях, требующих безусловного завершения. Допускается, что задача может прекратить любую задачу, включая себя.
Разделяемые переменные
Обычными средствами передачи данных между задачами являются операторы вызова и принятия входов.
Если две задачи считывают или изменяют разделяемую переменную (т. е. доступную обеим задачам переменную), то ни одна из них ничего не может знать о порядке выполнения этих операций над переменной, исключая точки синхронизации. Две задачи синхронизируются в начале и в конце их рандеву. В начале и в конце своей активизации задача синхронизируется с вызвавшей эту активизацию задачей. Задача, которая закончила свое выполнение, синхронизована с любой другой задачей.
О действиях, выполняемых программой, использующей разделяемые переменные, всегда могут быть сделаны следующие предположения:
Если в интервале времени между двумя точками синхронизации задача считывает разделяемую переменную скалярного или ссылочного типа, то эта переменная не изменяется никакой другой задачей в течение данного интервала времени.
Если в интервале времени между двумя точками синхронизации задача изменяет разделяемую переменную скалярного или ссылочного типа, то эта переменная не считывается и не изменяется никакой другой задачей в течение данного интервала времени.
Выполнение программы ошибочно, если какое-либо из этих предположений нарушено.
Если данная задача считывает значение разделяемой переменной, сделанные выше предположения допускают, чтобы реализация поддерживала локальные копии значения (например, в регистрах или в некоторых других видах временной памяти); и, пока данная задача не достигла точки синхронизации или не изменила значения разделяемой переменной, следствием принятых допущений является то, что для данной задачи чтение локальной копии эквивалентно чтению собственно разделяемой переменной.
Аналогично, если данная задача изменяет значение разделяемой переменной, сделанные предположения допускают, чтобы реализация поддерживала локальные копии значения и откладывала запоминание локальной копии в разделяемую переменную до точки синхронизации, заменяя каждые последующие считывание или изменение значения разделяемой переменной на считывание или изменение локальной копии. С другой стороны, не допускается, чтобы реализация вводила такую память, которая не будет обрабатываться в каноническом порядке (см. разд. 11.6).
Для задания того, что каждое считывание или изменение значения разделяемой переменной является для этой переменной точкой синхронизации, может быть использована прагма SHARED, т. е. для данной переменной (но не обязательно для остальных) сделанные выше предположения справедливы. Форма этой прагмы следующая:
pragma SHARED (простоетля_персменной);
Прагма допустима только для переменной, объявленной описанием объекта скалярного или ссылочного типа; описание переменной и прагма должны помещаться (в таком порядке) непосредственно в одном и том же разделе описаний или спецификации пакета; прагма должна появиться до любого вхождения имени переменной, отличного от вхождения в спецификаторе адреса.
Реализация должна ограничивать объекты, для которых допустима прагма SHARED, объектами, для которых каждое прямое считывание или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 БУФЕР;
СТРУКТУРА ПРОГРАММЫ И РЕЗУЛЬТАТ КОМПИЛЯЦИИ
В этой главе описываются общая структура программы и средства раздельной компиляции. Программа представляет собой набор из одного или нескольких компилируемых модулей, подаваемых на вход компилятора в виде одной или нескольких компиляций. Каждый компилируемый модуль определяет раздельную компиляцию некоторой конструкции. Ею может быть описание или тело подпрограммы, описание или тело пакета, описание или тело настраиваемого модуля или же конкретизация настройки. Кроме того, компилируемый модуль может быть субмодулем, т. е. телом подпрограммы, пакета, задачи или настраиваемого модуля, описанных внутри другого компилируемого модуля.
Компилируемые модули. Библиотечные модули
Текст программы может подаваться на вход компилятора в виде одной или нескольких компиляций. Каждая компиляция представляет собой последовательность компилируемых модулей.
компиляция : : = {компилируемый_модуль} компилируемый_модуль : : =
спецификатор—контекста библиотечный_модуль
I спецификатор_контекста вторичный_модуль библиотечный__модуль : : = описание_подпрограммы
I описание_пакета | описание_настройки
I конкретизация_настройки | тело_подпрограммы вторичный__модуль : : =
тело__библиотечного_модуля I субмодуль тело_библиотечного_модуля : : =
тело„подпрограммы | тело_пакета
Говорят, что компилируемые модули программы принадлежат программной библиотеке. Компилируемый модуль определяет библиотечный модуль или вторичный модуль. Вторичный модуль — это раздельно компилируемое соответствующее тело библиотечного модуля или субмодуль другого компилируемого модуля. Обозначением раздельно компилируемой подпрограммы (библиотечного модуля или субмодуля) должен быть идентификатор. В программной библиотеке простые имена всех библиотечных модулей должны быть различными идентификаторами.
Результат компилирования библиотечного модуля состоит в том, чтобы определить (или переопределить) его как модуль программной библиотеки. По правилам видимости каждый библиотечный модуль рассматривается как описание, приведенное непосредственно внутри пакета STANDARD.
Результат компилирования вторичного модуля состоит в том, чтобы определить тело библиотечного модуля или, в случае субмодуля, определить соответствующее тело программного модуля, описанного внутри другого компилируемого модуля.