В информатике , то цикл событий является программирование конструкт или шаблон дизайна , который ждет и отправляет события или сообщения в программе . Цикл событий работает, делая запрос к некоторому внутреннему или внешнему «провайдеру событий» (который обычно блокирует запрос до прибытия события), затем вызывает соответствующий обработчик событий («отправляет событие»). Цикл событий также иногда называют диспетчер сообщений , цикл обработки сообщений , насос сообщений , или цикл выполнения .
Цикл событий может использоваться вместе с реактором , если провайдер событий следует файловому интерфейсу , который может быть выбран или «опрошен» (системный вызов Unix, а не фактический опрос ). Цикл событий почти всегда работает асинхронно с отправителем сообщения.
Когда цикл событий формирует центральную конструкцию потока управления программы, как это часто бывает, его можно назвать основным циклом или основным циклом событий . Это название уместно, потому что такой цикл событий находится на самом высоком уровне управления в программе.
Передача сообщений [ править ]
Говорят, что насосы сообщений «перекачивают» сообщения из очереди сообщений программы (назначенной и обычно принадлежащей базовой операционной системе) в программу для обработки. В самом строгом смысле цикл событий - это один из методов реализации межпроцессного взаимодействия . Фактически, обработка сообщений существует во многих системах, включая компонент уровня ядра операционной системы Mach . Цикл событий - это особый метод реализации систем, использующих передачу сообщений .
Альтернативные конструкции [ править ]
Этот подход отличается от ряда других альтернатив:
- Традиционно программа просто запускалась один раз, а затем завершалась. Этот тип программ был очень распространен на заре компьютерных технологий и не обладал какой-либо формой взаимодействия с пользователем. Это до сих пор часто используется, особенно в форме программ, управляемых из командной строки . Любые параметры задаются заранее и передаются за один раз при запуске программы.
- Дизайн, управляемый меню. Они по-прежнему могут включать основной цикл, но обычно не рассматриваются как управляемые событиями в обычном смысле [ необходима цитата ] . Вместо этого пользователю предоставляется постоянно сужающийся набор опций, пока задача, которую он хочет выполнить, не станет единственной доступной опцией. Доступна ограниченная интерактивность через меню.
Использование [ править ]
Из-за преобладания графических пользовательских интерфейсов большинство современных приложений имеют основной цикл. Процедура get_next_message()
обычно предоставляется операционной системой и блокируется до тех пор, пока сообщение не станет доступным. Таким образом, цикл включается только тогда, когда есть что-то обрабатывать.
функция главная инициализировать () пока сообщение! = выйти сообщение: = get_next_message () process_message (сообщение) конец, пока конечная функция
Файловый интерфейс [ править ]
В Unix парадигма « все является файлом » естественным образом приводит к файловому циклу обработки событий. Чтение и запись в файлы, межпроцессное взаимодействие, сетевое взаимодействие и управление устройствами - все это достигается с помощью файлового ввода-вывода, при этом цель определяется файловым дескриптором . В выбери и опрос системных вызовов позволяют набор дескрипторов файлов , которые будут контролироваться на предмет изменения состояния, например , когда данные становятся доступными для чтения.
Например, рассмотрим программу, которая читает из постоянно обновляемого файла и отображает его содержимое в системе X Window , которая взаимодействует с клиентами через сокет (либо домен Unix, либо Berkeley ):
def main (): file_fd = open ( "logfile.log" ) x_fd = open_display () construct_interface () while changed_fds == select ({ file_fd , x_fd }): if file_fd in changed_fds : data = read_from ( file_fd ) append_to_display ( data ) send_repaint_message (), если x_fd в changed_fds : process_x_messages()
Обработка сигналов [ править ]
Одна из немногих вещей в Unix, которая не соответствует файловому интерфейсу, - это асинхронные события ( сигналы ). Сигналы поступают в обработчики сигналов , небольшие ограниченные фрагменты кода, которые выполняются, пока остальная часть задачи приостановлена; если сигнал получен и обработан во время блокировки задачи select()
, select вернется раньше с EINTR ; если сигнал получен, когда задача связана с ЦП , задача будет приостановлена между инструкциями, пока обработчик сигнала не вернется.
Таким образом, очевидный способ обработки сигналов состоит в том, чтобы обработчики сигналов устанавливали глобальный флаг и проверяли наличие флага в цикле событий непосредственно перед и после select()
вызова; если он установлен, обрабатывать сигнал таким же образом, как и с событиями в файловых дескрипторах. К сожалению, это приводит к возникновению состояния гонки : если сигнал поступает немедленно между проверкой флага и вызовом select()
, он не будет обработан до тех пор, пока не select()
вернется по какой-либо другой причине (например, будет прерван разочарованным пользователем).
Решение, к которому пришел POSIX, - это pselect()
вызов, который похож на вызов, select()
но принимает дополнительный sigmask
параметр, который описывает маску сигнала . Это позволяет приложению маскировать сигналы в основной задаче, а затем удалять маску на время select()
вызова, чтобы обработчики сигналов вызывались только тогда, когда приложение связано с вводом-выводом . Однако реализации pselect()
не всегда были надежными; версии Linux до 2.6.16 не имеют pselect()
системного вызова, [1] заставляя glibc имитировать его с помощью метода, склонного к тому же самому состоянию гонки, pselect()
призвано избежать.
Альтернатива, более портативное решение, является преобразование асинхронных событий на события на основе файлов , используя трюк самостоятельно трубы , [2] , где «обработчик сигнала записывает байт в трубу, другой конец которого контролируется select()
в основной программе». [3] В ядре Linux версии 2.6.22 signalfd()
был добавлен новый системный вызов , который позволяет получать сигналы через специальный файловый дескриптор.
Реализации [ править ]
Приложения Windows [ править ]
В операционной системе Microsoft Windows процесс, который взаимодействует с пользователем, должен принимать входящие сообщения и реагировать на них, что почти неизбежно происходит с помощью цикла сообщений в этом процессе. В Windows сообщение приравнивается к событию, которое создается и накладывается на операционную систему. Событием может быть, среди прочего, взаимодействие с пользователем, сетевой трафик, системная обработка, активность таймера, межпроцессное взаимодействие. Для неинтерактивных событий, связанных только с вводом-выводом, в Windows есть порты завершения ввода-вывода . Циклы портов завершения ввода-вывода выполняются отдельно от цикла сообщений и не взаимодействуют с циклом сообщений из коробки.
«Сердцем» большинства приложений Win32 является функция WinMain () , которая в цикле вызывает GetMessage () . GetMessage () блокируется до тех пор, пока не будет получено сообщение или «событие» (с функцией PeekMessage () в качестве неблокирующей альтернативы). После некоторой дополнительной обработки он вызывает DispatchMessage () , который отправляет сообщение соответствующему обработчику, также известному как WindowProc . Обычно сообщения, не имеющие специального WindowProc () , отправляются в DefWindowProc , используемую по умолчанию. DispatchMessage () вызывает WindowProc дескриптора HWND сообщения (зарегистрированного с помощью RegisterClass () функция).
Порядок сообщений [ править ]
Более поздние версии Microsoft Windows гарантируют программисту, что сообщения будут доставлены в цикл сообщений приложения в том порядке, в котором они были восприняты системой и ее периферийными устройствами. Эта гарантия важна при рассмотрении последствий проектирования многопоточных приложений.
Однако некоторые сообщения имеют другие правила, например сообщения, которые всегда принимаются последними, или сообщения с другим задокументированным приоритетом. [4]
X Window System [ править ]
Цикл событий Xlib [ править ]
X- приложения, использующие Xlib напрямую, построены на XNextEvent
семействе функций; XNextEvent
блокируется до тех пор, пока событие не появится в очереди событий, после чего приложение обработает его соответствующим образом. Цикл событий Xlib обрабатывает только события оконной системы; приложения, которым требуется возможность ожидания других файлов и устройств, могут создавать собственный цикл обработки событий из таких примитивов, как ConnectionNumber
, но на практике, как правило, используют многопоточность .
Очень немногие программы используют Xlib напрямую. В более общем случае наборы инструментов GUI на основе Xlib обычно поддерживают добавление событий. Например, в наборах инструментов на основе Xt Intrinsics есть XtAppAddInput()
и XtAppAddTimeout()
.
Обратите внимание, что вызывать функции Xlib из обработчика сигналов небезопасно, потому что приложение X могло быть прервано в произвольном состоянии, например, внутри XNextEvent
. См. [1] для решения для X11R5, X11R6 и Xt.
Цикл событий GLib [ править ]
Цикл событий GLib изначально создавался для использования в GTK +, но теперь используется и в приложениях без графического интерфейса пользователя, таких как D-Bus . Опрашиваемый ресурс - это набор файловых дескрипторов , которые интересуют приложение; блок опроса будет прерван, если поступит сигнал или истечет тайм-аут (например, если приложение определило тайм-аут или задачу ожидания). Хотя в GLib есть встроенная поддержка дескриптора файла и дочерних событий завершения, можно добавить источник событий для любого события, которое может быть обработано в модели подготовки-проверки-отправки. [2]
Библиотеки приложений, построенные на цикле событий GLib, включают GStreamer и методы асинхронного ввода-вывода GnomeVFS , но GTK + остается наиболее заметной клиентской библиотекой. События из оконной системы (в X считываются из сокета X ) переводятся GDK в события GTK + и излучаются как сигналы GLib на объектах виджетов приложения.
Циклы выполнения macOS Core Foundation [ править ]
Ровно один CFRunLoop разрешен для каждого потока, и может быть присоединено произвольно много источников и наблюдателей. Затем источники общаются с наблюдателями через цикл выполнения, при этом он организует очереди и отправку сообщений.
CFRunLoop забирается в какао в качестве NSRunLoop, что позволяет любое сообщение (эквивалент вызова функции в не- отражательных времени работы) , которые будут поставлены в очередь для отправки к любому объекту.
См. Также [ править ]
- Асинхронный ввод / вывод
- Событийно-ориентированное программирование
- Межпроцессного взаимодействия
- Передача сообщений
- Игровой цикл в программировании игр
Ссылки [ править ]
- ^ "Linux_2_6_16 - новички в ядре Linux" . kernelnewbies.org . Источник 2021-03-03 .
- ^ DJ Bernstein. "Уловка с трубкой" .
- ^ ОШИБКИ,: мультиплексирование синхронного ввода / вывода - Руководство программиста Linux - Системные вызовы
- ^ Функция GetMessage () со списком приоритетов сообщений.
Внешние ссылки [ править ]
- Блуждание по лабиринту маршрутизации сообщений и команд MFC
- Использование сообщений и очередей сообщений (MSDN)
- Использование оконных процедур (MSDN)
- WindowProc (MSDN)