Системы, управляемые событиями
Специальная программа, менеджер событий (рис. 7.12), просматривает очередь и передает поступающие события обработчикам. События, связанные с экранными координатами, передаются обработчику, ассоциированному с соответствующим окном. Клавиатурные события передаются фокусу клавиатуры – текущему активному обработчику клавиатуры.
Управление событиями позволяет относительно легко разрабатывать динамичные пользовательские интерфейсы, привычные для пользователей современных графических оболочек.
Высокая динамичность интерфейса проще всего обеспечивается, если каждый обработчик быстро завершается. Если же в силу природы запрашиваемой операции она не может завершиться быстро – например, если мы вставили символ, параграф удлинился на строку, и в результате текстовому процессору Типа WYSIWYG приходится переформатировать и переразбивать на страницы весь последующий текст – мы можем столкнуться с проблемой.
Рис. 7.12. Менеджер и обработчики событий
В такой ситуации (а при написании реальных приложений она возникает сплошь и рядом) мы и вынуждены задуматься о том, что же в действительности представляют собою обработчики – процедуры, синхронно вызываемые единственной нитью менеджера событий, или параллельно исполняющиеся нити. Первая стратегия называется синхронной обработкой сообщений, а вторая, соответственно, – асинхронной.
Графические интерфейсы первого поколения – Mac OS, Win16 – реализовывали синхронную обработку сообщений, а когда обработчик задумывался надолго, рисовали неотъемлемый атрибут этих систем – курсор мыши в форме песочных часов.
Несколько более совершенную архитектуру предлагает оконная подсистема OS/2, Presentation Manager. PM также реализует синхронную стратегию обработки сообщений (менеджер событий всегда ждет завершения очередного обработчика), но в системе, помимо менеджера событий, могут существовать и другие нити. Если обработка события требует длительных вычислений или других действий (например, обращения к внешним устройствам или к сети), рекомендуется создать для этого отдельную нить и продолжить обработку асинхронно. Если же приложение этого не сделает (например, обработчик события просто зациклится или заснет на семафоре), системная очередь сообщений будет заблокирована и ни одно из графических приложений не сможет работать. Современные версии РМ предоставляют в этом случае возможность отцепить "ненормальное" приложение от очереди или паже принудительно завершить его.
Асинхронные очереди сообщений предоставляют Win32 и оконная система X Window. Впрочем, и при асинхронной очереди впавший в философские размышления однопоточный обработчик событий – тоже малоприятное зрелище, ведь он не может перерисовать собственное окно, поэтому передвижение других окон по экрану порождает любопытные спецэффекты (к сожалению, запечатлеть эти эффекты при помощи утилит сохранения экрана невозможно – все известные автору средства дожидаются, пока все попавшие в сохраняемую область окна перерисуются. А фотографии монитора обычно имеют низкое качество). Разработчикам приложений для названных систем также рекомендуется выносить длительные вычисления в отдельные нити.
Большинство реальных приложений для современных ОС, имеющих пользовательский интерфейс, таким образом, имеют двух – или более слойную архитектуру. При этом архитектура ближайшего к пользователю стоя (frontend), как правило, тяготеет к событийно-управляемой, а следующие слои (backend) обычно состоят из более традиционных взаимодействующих (не всегда, впрочем, строго гармонически) параллельно исполняющихся нитей, зачастую даже разнесенных по разным вычислительным системам.