Rationale for Ada 2005: Tasking and Real-Time
RUSTOPBACKNEXT
ENG |
3. Synchronized interfaces
@ We now turn to the most important improvement to the core tasking features introduced by Ada 2005. This concerns the coupling of object oriented and real-time features through inheritance. @ Recall from the paper on the object oriented model that we can declare an interface thus
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale for Ada 2005: Tasking and Real-Time
@ENGRUSTOPBACKNEXT3. Синхронизированные интерфейсы
@ Теперь мы обращаемся к самому важному усовершенствованию в области управления задачами введенному Адой 2005. Это касается связи объектно-ориентированных средств и средств работы в реальном времени через наследование.
@ Возвращаясь к статье описывающей объектно-ориентированную модель мы можем объявить интерфейс таким образом:
|
@ Интерфейс это по существу абстрактный теговый тип, который не может иметь никаких компонентов, но может иметь абстрактные операции и нулевые процедуры. Тогда мы можем получить другие интерфейсы и теговые типы через наследование:
|
@ Напомним, что теговый тип может быть унаследован, в основном, из другого нормального тегового типа, но может также быть получен из нескольких интерфейсов. В списке наследования первое место занимает предок называемый родителем (это может быть нормальный теговый тип или интерфейс), а другие предки занимающие последующие позиции (которые можут быть только интерфейсами), назваются прародителями.
@ Ада 2005 вводит новые категории интерфейсов именуемые sinchronized, protected, и task. Синхронизированный интерфейс может быть осуществлен или задачей или защищённым типом; защищенный интерфейс может быть осуществлен только защищенным типом, а интерфейс задачи может быть осуществлен только задачным типом.
@ Неограниченный интерфейс может быть осуществлен только неограниченным типом. Однако, явно отмеченный ограниченный интерфейс может быть осуществлен любым теговым типом (ограниченным или нет) или задачным типом или защищенным. Напомним, что задачи и защищенные типы неотъемлемо ограничены. Отметим, что мы используем термин ограниченный интерфейс чтобы обратиться ко всем интерфейсам отмеченным как limited, sinchronized, task или protected, и мы используем явно limited, чтобы обратиться к фактически отмеченным как ограниченным.
@ Таким образом, мы можем написать:
|
@ и мы можем обеспечить операции, которые должны быть абстрактными или нулевыми. (Напомним, что synchronized - новое зарезервированное слово). Мы можем составить эти интерфейсы при условии, что никакой конфликт не возникает. Следующее всё разрешено:
|
@ Существует простое правило которое гласит: чтобы составить два или более интерфейса необходимо чтобы мы не смешивали задачу и защищенные интерфейсы, и ни один из интерфейсов предков в иерархии не должен быть limited, synchronized, task или protected.
@ Мы можем получить реальный тип задачи или защищенный тип из одного или более соответствующих интерфейсов:
|
@ или
|
@ В отличие от теговых типов записи мы не можем получить задачу или защищенный тип от другой задачи или защищенного типа также. Таким образом, иерархия наследования может быть только на один уровень глубже чем уровень на котором мы объявляем фактическую задачу или защищенный тип.
@ Операции этих различных интерфейсов объявляются обычным способом, и у интерфейса, составленного из нескольких интерфейсов, есть операции всех их с теми же самыми правилами относительно дублирования, и отмена абстрактной операции нулевым и так далее что касается нормальных теговых типов.
@ Когда мы объявляем фактическую задачу или защищенный тип, тогда мы должны осуществить все операции заинтересованных интерфейсов. Это может быть сделано двумя способами, или объявляя точку входа или защищенную операцию в спецификации задачи или защищённый объект или объявляя отличную подпрограмму в том же самом списке объявлений (но не оба вместе). Конечно, если операция - пустой указатель тогда, она может быть наследована или отменена (overridden) как обычно.
@ Таким образом, интерфейс:
|
@ может быть реализован так:
|
@ или так:
|
@ или даже так:
|
@ В последнем случае нет никаких входов и, таким образом, у нас есть сопоставление с концом, который несколько подобен сопоставлению, конец, который происходит с generic пакетами, используемыми как сигнатуры.
@ Обратите внимание, как первый параметр, который обозначает задачу, опущен, если это осуществлено входом.
@ Это демонстрирует новую префиксную нотацию для вызова операции теговых типов вообще. Вместо того чтобы писать:
|
@ мы можем написать:
|
@ при условии что X - теговый тип и Op - примитивная операция этого типа.
@ Для реализации операции интерфейса посредством точки входа задачного типа или защищенной операцией защищенного типа необходимо соблюдение нескольких довольно очевидных условий.
@ Во всех случаях первый параметр операции интерфейса должен быть типом задачи или защищенным типом (это может быть ссылочный параметр).
@ Кроме того, в случае защищенного типа, у первого параметра операции, осуществленной защищенной процедурой или входом, должен быть режим или в (и в случае ссылочного параметра это должна быть ссылка на параметр переменную).
@ Если операция не соответствует этим правилам тогда, она должна быть осуществлена как подпрограмма. Важный пример - то, что функция должна быть осуществлена как функция в случае типа задачи, потому что нет такой вещи как функциональный вход. Однако, функция может часто непосредственно осуществляться как защищенная функция в случае защищенного типа.
@ Входы и защищенные операции, которые осуществляют наследованные операции, могут быть в видимой части или приватной части задачи или защищенного типа таким же образом это касается теговых типов записи.
@ Может казаться довольно странным, что операция может быть осуществлена подпрограммой, которая не является частью задачи или защищенного типа непосредственно - кажется, как будто это не может быть сейф задачи в некотором роде. Но общая парадигма состоит в том, где операция как абстракция должна быть осуществлена двумя или больше вызовами входа.
@ Пример происходит в некоторых реализациях классической проблемы читения и записи, которую мы будем рассматривать позже.
@ Конечно, у задачи или защищенного типа, который осуществляет интерфейс, могут быть дополнительные входы и операции также как у полученного тегового типа может быть больше операций чем у его родителя.
@ Замена индикаторов overriding и not overriding могут быть применены к входам так же как к процедурам. Таким образом, пакет PT2 рассмотренный выше мог бы быть написан как:
|
@ Теперь рассмотрим простой пример чтения и записи, чтобы проиллюстрировать различные аспекты. Мы начнём со следующего интерфейса:
|
@ Цель здесь состоит в том, что интерфейс описывает абстракцию инкапсуляции скрытого местоположения и средств записи и чтения значения некоторого типа Item.
@ Мы могли осуществить это несинхронизированным способом так:
|
@ Эта реализация - конечно не сейф задачи (сейф задачи иногда упоминается как сейф потока). Если задача в момент вызова Write будет прервана в процессе изменения типа Item являющемся составным, и при этом будет вызвана функция Read может получится весьма любопытный результат, состоящий из части нового значения и части старого значения.
@ Для иллюстрации мы могли получить синхронизированный интерфейсный тип Sync_RW синхронизированный интерфейс и RW; Этот интерфейс может быть осуществлен только задачей или защищённым типом. Для защищенного типа мы могли бы иметь:
|
@ Обратите внимание, как первый параметр операций интерфейса опущен, когда они осуществлены защищенными операциями.
@ Эта реализация - отлично сейф задачи. Однако, одна из особенностей примера чтения и записи состоит в том, что весьма безопасно позволить многжественный доступ для чтения, так как они не могут навредить друг другу. Но тип Prot_RW не разрешает множественное чтение, потому что защищенные процедуры могут быть выполнены только одной задачей за один раз.
@ Теперь рассмотрим:
|
@ В этой реализации процедура Read осуществлена процедурой вне защищенного типа, и эта процедура тогда вызывает функцию Read в пределах защищенного типа. Это разрешает множественное чтение, потому что одна из особенностей защищенных функций состоит в том, что массовое обращение разрешено (но конечно запросы защищенной процедуры Write блокируются, в то время вызовов защищенной функции). Структура подчеркнута при помощи индикатора overriding.
@ Простая реализация управления задачами могла бы быть следующая:
|
@ Наконец, вот реализация управления задачами, которая разрешает множественное чтение и гарантирует, что начальное значение установлено разрешая сначала только вызов Write. Этот пример взят из учебника [3].
|
@ В этом случае к защищаемым данным обращаются через ссылочный дискриминант задачи. Это структурировано так чтобы процедура Read могла читать данные непосредственно. Отметим также, что процедура Read (которая является реализацией процедуры Read интерфейса) вызывает две точки входа задачи.
@ Заметим, что этот последний пример приведён только для иллюстрации. Как известно, атрибут Count, используемый в задачах (в противоположность защищенным объектам), может вводить в заблуждение, если задачи прерываются или если запросы входа установлены. Кроме того, это было бы ужасно медленно.
@ Таким образом мы видим, что ограниченный интерфейс, такой как RW мог бы быть осуществлен нормалным теговым типом (плюс его различные операции) и защищенным типом и также типом задачи. Мы могли тогда послать операциям любого из них согласно тэгу заинтересованного типа. Заметим, что задача и защищенные типы - теперь другие формы теговых типов и, таким образом, мы должны делать все возможное сказать теговый тип записи (или неофициально, нормалный теговый тип) где это возможно.
@ В вышеупомянутом примере, типы Simple_RW, Prot_RW, Multi_Prot_RW, Task_RW и Multi_Task_RW все реализация интерфейса RW.
@ Таким образом, мы могли бы иметь:
|
@ и согласно значению в RW_Ptr это могло бы вызвать соответствующий вход или процедуру объекта любого из типов, осуществляя интерфейс RW.
@ Однако, если мы имеем:
|
@ тогда мы знаем, что любая реализация синхронизированного интерфейса, Sync_RW будет сейфом задачи, потому что это может только быть осуществлено задачей или защищённым типом. Так запрос диспетчеризации:
|
@ будет сейф задачи.
@ Интересный момент состоит в том, что, потому что запрос диспетчеризации мог бы быть к входу или к процедуре, мы теперь разрешаем то, что, кажется, вызовы процедуры в установленных запросах входа, если они могли бы послать входу.
@ Таким образом мы могли иметь:
|
@ Конечно это могло бы послать процедуре Read, если заинтересованный тип оказывается Simple_RW, когда time out не может произойти. Но если бы это послало вход Read типа Task_RW тогда, то это может произойти time out.
@ С другой стороны нам не разрешено использовать установленный вызов, если это, как статически известно, процедура.
@ Так:
|
@ не разрешено.
@ Примечание предостережения то, чтобы. Помните, что время - к тому, когда запрос принят. Если это посылает Multi_Task_RW.Read тогда, время никогда не случается, потому что само Read - процедура и вызвано сразу. Однако, негласно это вызывает два входа и, занимают много времени - также. Но если бы мы вызвали эти два входа непосредственно с установленными запросами тогда, то мы вывели бы время, если бы была летаргическая программа записи в продвижении. Таким образом обертка искажает абстракцию. В некотором смысле это не является намного худшим чем проблема, которую мы имеем так или иначе, который время - к тому, когда запрос принят а не к тому, когда это возвращается - это могло едва быть иначе.
@ Те же самые правила относятся к условным запросам входа и также к асинхронным операторам выбора, где оператор вызова может быть запросом диспетчеризации.
@ Подобным способом мы также разрешаем установленные запросы к входам, переименованным как процедуры. Но отметим, что мы не позволяем установленные запросы к универсальным формальным подпрограммам даже при том, что они могли бы быть осуществлены как входы.
@ Другой важный пункт состоит в том, что мы можем как обычно принять общие свойства заинтересованного класса. Таким образом, в случае интерфейса задачи мы знаем, что это должно быть осуществлено задачей и так операции, такие как аварийное прекращение работы, и атрибутов Identity, Callable и так далее, может быть применена. Если мы знаем, что интерфейс синхронизирован тогда, мы действительно знаем, что он должен быть осуществлен задачей или защищенным типом и сейф задачи - также.
@ Обычно интерфейс осуществляется задачей или защищённым типом, но он может также быть осуществлено задачей единичного предмета или защищённым объектом несмотря на то, что у единичных предметов нет никакого имени типа. Таким образом, мы могли бы иметь:
|
@ с очевидным телом. Однако мы не могли объявить единственный защищенный объект, подобный типу Multi_Prot_RW рассмотренному выше. Это потому что мы нуждаемся в имени типа, чтобы объявить отмену (overriding) процедуру Read вне защищенного объекта. Таким образом, реализации единичного предмета возможны при условии, что интерфейс может быть осуществлен непосредственно задачей или защищённым объектом без внешних подпрограмм.
@ Вот другой пример:
|
@ может быть реализован так:
|
@ Есть довольно очевидное правило о приватных типах и синхронизированных интерфейсах. И частичное и полное представление должно быть синхронизировано или нет. Таким образом, если мы написали:
|
@ тогда полный тип T должен быть типом задачи или защищенным типом или возможно синхронизированным, защищенным или интерфейсом задачи.
@ Мы заключаем это обсуждение по интерфейсам, говоря несколько слов об использовании слова limited. (Большой части этого уже обсуждалось в статье посвящённой объектно-ориентированной модели, но это стоит повторить в контексте параллельных типов). Мы всегда явно вставляем limited, synchronized, task, или protected в случае ограниченного интерфейса, чтобы избежать беспорядка. Чтобы получить новый явно ограниченный интерфейс из существующего ограниченного интерфейса LI мы пишем:
|
@ тогда как в случае нормальных типов мы можем написать:
|
@ тогда LT2 ограничен по нормальным правилам наследования. Типы берут свою ограниченность (limitedness) от своего родителя (первый в списке, если это не прародитель), и это не должно быть дано явно при наследовании типа - хотя это может быть в Аде 2005 таким образом:
|
@ Помните важное правило, что все потомки неограниченного интерфейса должны быть неограничены, потому что иначе ограниченные типы могли закончиться с операцией присваивания.
@ Это означает, что мы не можем написать:
|
@ Это незаконно, потому что интерфейс NLI в объявлении задачи типа TT не ограничен.
2010-10-24 00:26:55
. .