учебники, программирование, основы, введение в,

 

Событийное программирование

Событие, сообщение, демон
Упомянутое ранее качество
Подготовка информации для действий в ходе распознавания условий применимости действий, пожалуй, является критическим моментом для разделения случаев использования сентенциального и событийного программирования. Проверка приоритетов и выбор действия с наивысшим приоритетом в качестве активного точно так же, как отождествление метавыражения в сентенциальном случае, запрятана в атомарные действия программной системы, но при проверке приоритетов программист не получает никакой полезной информации для проведения выбранного действия. Если какая-то полезная информация и получается будущим действием, то она задается отдельно и императивно, в параметрах сообщения, вызывающего событие. В свою очередь, наличие либо практическое отсутствие такой информации определяет разницу двух ипостасей событийного программирования: программирования от событий и от приоритетов.
Следует заметить, что есть еще одна особенность, общая для двух стилей: сентенциального и событийного.
Отделение проверки условий от выполнения действий
Это представляется общей характеристикой технологических решений для стилей, где условия глобальны.
До сих пор мы ничего не говорили о том, какие события возможны при программировании в событийно-ориентированном стиле. Исторически этот стиль как определенный художественный прием сформировался в области разработки операционных систем, где естественно связывать понятие события с прерываниями. Прерывание - это сигнал от одного из устройств (может быть, и от самого процессора), который говорит о том, что произошло нечто, на что следует обратить внимание. Когда происходит прерывание, операционная система распознает, какая причина его вызвала, и далее формирует событие как информационный объект, вызывающий реакцию программной системы. Возможны разные способы реагирования на события, в том числе и передача его для обработки той программе, при выполнении которой возникло прерывание, породившее это событие.
Из потребности такой обработки, собственно говоря, и сформировался событийно-ориентированный стиль программирования. Наиболее очевидная область его адекватного применения - реализация интерактивных взаимодействий программы с пользователем и решение других подобных задач (например, тех, которые требуют опроса датчиков состояния каких-либо технологических процессов).
В частности, при взаимодействии с пользователем чаще всего достаточно таких событий, как нажатие на клавишу, перемещение курсора мыши, указание световой кнопки и т. п. Не случайно именно те прерывания, которые способна перенаправить операционная система для обработки на уровень пользовательской программы, стали основой для выработки систем событий, реакция на которые задается в событийно-ориентированном стиле. События этого рода можно передать от одного обработчика к другому, не нарушая условий адекватного применения данного стиля: для этого достаточно объявить такое перенаправление события порождением нового события.
Но требуется также генерация событий, не связанных с прерываниями. К примеру, программируя в событийно-ориентированном стиле, естественно объявить событием ситуацию, когда значение одной переменной становится больше другой. Однако такого рода свойства вычислительного процесса никак не отражаются в системе прерываний, а потому обычные языковые средства событийно-ориентированного программирования (например, в Delphi), часто провозглашаемые как универсальные, здесь не помогут. Необходимы иные механизмы, которые уводят программиста из области шаблонов проектов, стандартизующих обработку событий.
Об этом обстоятельстве, ограничивающем применимость подхода, предпочитают умалчивать, и, как обычно бывает в подобных ситуациях, при беглом знакомстве с соответствующими средствами могут возникнуть ни на чем не основанные ожидания с последующими разочарованиями и даже отторжением стиля, хорошо работающего на своем месте.
Примером языка, до некоторой степени ориентированного на событийно-ориентированное программирование, может служить Perl.
Событийный стиль удобен не только на уровне конкретного программирования. Часто концептуальное осмысление задачи полезно проводить в терминах системы событий, а уж затем решать, как реализовывать систему: непосредственно использовать события как средство управления, моделировать событийное взаимодействие имеющимися средствами или вообще отказаться от этого механизма в данной разработке.
В качестве иллюстрации этого тезиса опишем с позиций событийного стиля программирования взаимоотношения между синтаксическим анализом и вычислениями семантики программы. Реализация этих взаимоотношений - обычная задача, решаемая при разработке любого транслятора. В событийном описании этой задачи в качестве событий системы естественно рассматривать распознавание во входном потоке (транслируемом тексте) синтаксических единиц, то есть конструкций языка. Такое событие требует в качестве обработчика семантическую подпрограмму, связанную с распознаваемой конструкцией. Следовательно, выявлены два автономных процесса: синтаксический анализ, задающий генерацию событий, и семантическая обработка, осуществляемая как последовательность вызовов семантических подпрограмм-реакций, упорядоченная событиями. Коль скоро есть концептуальное разделение процессов, его можно воплотить в реальной системе в событийном стиле. Это решение влечет за собой следующее.

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

При событийном программировании разделение процессов генерации и обработки событий влечет за собой их полную независимость. В практике программирования транслирующих систем это качество считается не очень удобным, и вместо событийного механизма обычно используется схема сопрограммного взаимодействия с так называемым синтаксическим управлением: синтаксический анализатор сам вызывает нужные семантические программы, когда распознает необходимость сделать это (в событийном стиле это означает генерацию соответствующего события). Нет никакого противоречия между двумя схемами, и единственное различие между ними в том, что при синтаксическом управлении ожидание события оказалось возможным подменить прямым вызовом подпрограммы. Иными словами, в схеме трансляции удается статически, до выполнения программы вычислить события, наступление которых требует вызова их обработчиков (семантических подпрограмм). Вообще, сопрограммное взаимодействие можно трактовать как статически вычисленную событийность.
Разумеется, рамки событийного программирования много шире тех случаев, когда можно статически определять необходимость активизации обработчика. Полезно объединение генерации событий с их обработкой, и за счет него можно растворять события в программе. В частности, и по этой причине следует рассматривать событийное программирование как самостоятельный стиль. Но интересны и такие случаи, когда объединение, хотя и возможно, но не делается. Конкретный пример - XML/XSL технология, для которой разделение структуры и интерпретации текста считается принципиальным: это позволяет строить съемные системы обработки, иметь несколько независимых таких систем для разного назначения. В своей сфере применения многовариантность имеет большие преимущества, но, как всегда, перенос принципов данной технологии куда угодно чреват уже не раз отмеченной неадекватностью.

http://localhost:3232/img/empty.gifПрограммирование от приоритетов

Обсуждая событийное программирование, мы упомянули о том, что при программировании в этом стиле может возникнуть потребность в упорядочивании выполнения нескольких конкурирующих между собой реакций на события. Достаточно универсальным средством удовлетворения этой потребности является приписывание реакциям приоритетов: сначала выполняются те реакции, которые имеют больший приоритет. Этот прием, как выявилось на практике, пригоден для решения достаточно широкого круга задач. Он стал основой для формирования самостоятельного варианта событийного стиля: программирования от приоритетов.
Данная ипостась стиля порождена практическими потребностями. Она предназначена для организации работы многих взаимодействующих процессов, динамически порождаемых и исчезающих. Фундаментальных теоретических исследований в области программирования от приоритетов почти нет. В частности, в классической работе Хоара, в которой рассматривается управление взаимодействующими процессами, предполагается, что программа написана в традиционном структурном стиле и никаких попыток приспособить ее к обстановке, где имеется много процессов, нет.
Стиль программирования от приоритетов не реализован систематически в виде законченного языка, но часто используется в прикладных языках скриптов для управления процессами или событиями либо в распределенных системах, либо на полуаппаратном уровне (так называемые встроенные программы, являющиеся частью специализированного прибора или устройства).
В программировании от приоритетов, как и в сентенциальном программировании, порядок расположения операторов в программе не играет принципиальной роли, зато важен приоритет оператора, то есть некоторое значение, принадлежащее в самом общем случае частично-упорядоченному множеству и почти всегда рассматриваемое как элементарное. После завершения очередного оператора среди оставшихся выбирается оператор с максимальным приоритетом. Если таких операторов с равными или несравнимыми приоритетами несколько, то, вообще говоря, в идеале надо было бы выбирать один из них недетерминированно.
Об операторах, имеющих приоритеты, можно говорить, когда программирование от приоритетов реализовано в специально предназначенном для этого стиля языке. Если же приходится моделировать программирование от приоритетов в обычном языке, то в качестве таких операторов могут выступать иные структурные единицы текста программы. В объектно-ориентированном языке, как правило, это методы объектов. Для краткости, в рамках настоящего параграфа мы будем говорить об операторах, имея в виду только что сделанное замечание.
При назначении приоритетов важно различать случаи, когда они задаются для операторов как элементов текста программы и когда приоритеты вводятся для динамических структурных единиц, то есть для тех элементов процесса вычислений, которые возникают в ходе выполнения программы, но связаны с тем или иным оператором, приоритет которого определяется в рамках конкретного экземпляра (например, объекта). Таким образом выстраиваются разные системы приоритетов, причем во втором случае система приоритетов вынужденно оказывается динамической.
На практике обычно используют жесткую дисциплину, базирующуюся на порядке операторов в программе. Часто управление по приоритетам соединяется с так называемой системой демонов, то есть процедур, вызываемых при выполнении некоторого условия, а не в тот момент, когда при движении по тексту программы мы подошли к их явному вызову (сидит такой демон в засаде и выжидает момент, когда можно будет начать исполняться). И текущий «нормальный» процесс, и проснувшиеся демоны имеют свои приоритеты, и демон начнет исполняться, даже если он активизирован, лишь в том случае, если среди активных не осталось процесса с большим, чем у демона, приоритетом. Эта схема реализована, в частности, в системе UNIX.
Уместно отметить, что событийно-ориентированное программирование порою может рассматриваться как вырожденный случай стиля программирования от приоритетов: для каждого экземпляра структурной единицы, способной реагировать на события, выставляется (бесконечно большой) приоритет, если этот экземпляр фактически должен активизировать реакцию, и не выставляется приоритет (выставляется бесконечно малый приоритет), если экземпляр не должен реагировать на событие. Общий случай стиля событийно-ориентированного программирования также близок к стилю программирования от приоритетов, если считать, что всем обработчикам события, которые должны быть выполнены при его появлении, присвоены приоритеты, соответствующие фактическому порядку их выполнения. Когда такой порядок неизвестен, можно считать, что всем активизируемым обработчикам присвоены равные приоритеты.
Кардинальное различие между программированием от событий и от приоритетов состоит в принципах задания управления. В случае программирования от событий установлена прямая связь между событием и реакцией: если условие срабатывания открывает для обработчика возможность быть выполненным, то он обязательно будет выполнен в ответ на соответствующее событие. При программировании от приоритетов ситуация иная: задавая демону даже наивысший приоритет, можно лишь надеяться на то, что он сработает раньше других, причем, возможно, даже без появления какого бы то ни было события. Если абстрагироваться от механизма управления при оперировании с приоритетами и событиями, то можно считать эти два стиля вариантами одной сущности.
Варианты событийного программирования от событий и приоритетов хорошо совмещаются, взаимно дополняя друг друга, когда целесообразна следующая архитектурная схема разработки программного проекта. От событий строятся фрагменты системы, которые отвечают за реакцию на каждое из событий, реализуемую разными обработчиками, а программирование этих обработчиков ведется от приоритетов.
Рассмотрим написанный в двух вариантах на двух условных языках пример программы с приоритетами.
3:  Цикл пока не будет все сделано
{
5:  Подготовка данных;
10: Обработка текущих изменений;
}
4:  Демон {Если Поступили данные То
Прием рутинных данных};
8:  Демон {Если Поступили экстренные данные То
Запоминание экстренных изменений};
12: Демон {Если Авария То Аварийные действия};
Пример 13.2.1.
a: PRIO 3  {Prepare Data; SLEEP};
b: PRIO 10 {Process Changes; SLEEP};
c: PRIO 8  DAEMON IF Extra Data
THEN Store Extra Data;
AWAKE b FI;
d: PRIO 12 DAEMON IF Alert
THEN Emergency; FI;
e: PRIO 2  DAEMON IF Idle
THEN AWAKE a; FI;
Пример 13.2.2.
Видно, что в данном примере все приоритеты задавались статически. Именно к этому стремятся почти во всех случаях современной практики вычислений с приоритетами. Однако полной статичности можно добиться лишь в простейших случаях. Например, если процесс долго ждет своей очереди на исполнение, то естественно постепенно поднимать его приоритет. При этом приоритет такого «пользовательского» действия может превзойти приоритет системного процесса, и остается либо статически задавать каждому процессу допустимый максимальный приоритет, и мы вновь попадаем в ту же ловушку, либо задавать разным классам процессов приоритеты, просто не сравнимые по порядку, чтобы ни при каком нормальном исполнении приоритет пользовательского процесса не дорос, скажем, до 109, за которым начинаются приоритеты срочных системных демонов.
Заметим, что назначение приоритетов операционной системой, «сверху», принципиально ничего в данной картине не меняет. С точки зрения пользовательских программ эти приоритеты все равно статические.
Рассмотрим теоретически, какие классы программ можно описать при помощи статических и динамических приоритетов. Оказывается, при помощи статических приоритетов, являющихся натуральными числами, невозможно описать даже примитивно-екурсивные композиции исходных действий.
Таким образом, концептуально натуральных чисел недостаточно для описания приоритетов, но оказывается, что для них хватает ординалов (см. курс математической логики). Поскольку ординалы до ε0 имеют хорошую вычислительную модель, можно рассматривать целочисленные приоритеты как конкретную реализацию абстрактного класса таких ординалов.
В случае реально распределенных или параллельных действий проблема приоритетов еще важнее и сложнее. Часто управление по приоритетам - единственный шанс справиться со сложностью такой нелинейной во времени системы. Тут уже и линейно упорядоченного множества приоритетов не хватает.
В целом программирование от приоритетов является мощным, но специфическим орудием для описания глобальных совместных, параллельных или распределенных процессов. Его элементы проникают и в традиционные системы программирования в виде обработки исключительных ситуаций в программах. Здесь, как и в программировании от событий с обособленными реакциями, всего два приоритета, которые налагаются на структуру использующей конструкции. Противоречивость этого средства со структурным программированием только в том, что не всегда ясно, что делается далее, когда обработка исключения завершается.
http://localhost:3232/img/empty.gifhttp://localhost:3232/img/empty.gif

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика