stringtranslate.com

Цикл событий

В информатике цикл событий — это программная конструкция или шаблон проектирования , который ожидает и отправляет события или сообщения в программе . Цикл событий работает, отправляя запрос какому-либо внутреннему или внешнему «поставщику событий» (который обычно блокирует запрос до тех пор, пока не произойдет событие), а затем вызывает соответствующий обработчик событий («отправляет событие»). Цикл событий также иногда называют диспетчером сообщений , циклом сообщений , насосом сообщений или циклом выполнения .

Это также распространенный метод, используемый для реализации таких серверов, как веб-серверы .

Цикл событий можно использовать в сочетании с реактором , если поставщик событий следует за файловым интерфейсом, который можно выбрать или «опросить» (системный вызов Unix, а не фактический опрос ). Цикл событий почти всегда работает асинхронно с отправителем сообщения.

Когда цикл событий образует центральную конструкцию потока управления программой, как это часто бывает, его можно назвать основным циклом или главным циклом событий . Это название уместно, поскольку такой цикл событий находится на самом высоком уровне управления внутри программы.

Передача сообщений

Говорят, что насосы сообщений «перекачивают» сообщения из очереди сообщений программы (назначенной и обычно принадлежащей базовой операционной системе) в программу для обработки. В самом строгом смысле цикл событий — это один из методов реализации межпроцессного взаимодействия . Фактически обработка сообщений существует во многих системах, включая компонент уровня ядра операционной системы Mach . Цикл событий — это особый метод реализации систем, использующих передачу сообщений .

Альтернативные конструкции

Этот подход отличается от ряда других альтернатив:

Применение

Из-за преобладания графических пользовательских интерфейсов большинство современных приложений имеют основной цикл. Процедура get_next_message()обычно предоставляется операционной системой и блокируется до тех пор, пока не станет доступно сообщение. Таким образом, вход в цикл осуществляется только тогда, когда есть что обрабатывать.

функция главная инициализировать() пока сообщение != выйти сообщение:= get_next_message() процесс_сообщение (сообщение) конец  , пока функция завершения 

Файловый интерфейс

В Unix парадигма « все есть файл » естественным образом приводит к циклу событий на основе файла. Чтение и запись в файлы, межпроцессное взаимодействие, сетевое взаимодействие и управление устройством выполняются с помощью файлового ввода-вывода, при этом цель идентифицируется файловым дескриптором . Системные вызовы select и poll позволяют отслеживать набор файловых дескрипторов на предмет изменения состояния, например, когда данные становятся доступными для чтения .

Например, рассмотрим программу, которая читает постоянно обновляемый файл и отображает его содержимое в системе X Window , которая взаимодействует с клиентами через сокет (либо домен Unix , либо Berkeley ):

def  main ():  file_fd  =  open ( "logfile.log" )  x_fd  =  open_display ()  struct_interface ()  while  True :  rlist ,  _ ,  _  =  select . select ([ file_fd ,  x_fd ],  [],  []):  если  file_fd  в  rlist :  data  =  file_fd . read ()  append_to_display ( data )  send_repaint_message ()  если  x_fd  в  rlist :  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(), позволяющий получать сигналы через специальный файловый дескриптор.

Реализации

HTML/Яваскрипт

Веб-страница и ее JavaScript обычно выполняются в однопоточном процессе веб-браузера . Процесс браузера обрабатывает сообщения из очереди по одному. С данным сообщением может быть связана функция JavaScript или другое событие браузера. Когда процесс браузера завершает обработку сообщения, он переходит к следующему сообщению в очереди.

Windows-приложения

В операционной системе Microsoft Windows процесс, который взаимодействует с пользователем, должен принимать входящие сообщения и реагировать на них, что почти неизбежно происходит с помощью цикла обработки сообщений в этом процессе. В Windows сообщение приравнивается к событию, созданному и наложенному на операционную систему. Событием может быть, среди прочего, взаимодействие пользователя, сетевой трафик, обработка системы, активность таймера, межпроцессное взаимодействие. Для неинтерактивных событий, предназначенных только для ввода-вывода, в Windows предусмотрены порты завершения ввода-вывода . Циклы портов завершения ввода-вывода выполняются отдельно от цикла сообщений и не взаимодействуют с циклом сообщений «из коробки».

«Сердцем» большинства приложений Win32 является функция WinMain(), которая в цикле вызывает GetMessage(). GetMessage() блокируется до тех пор, пока не будет получено сообщение или «событие» (с функцией PeekMessage() в качестве неблокирующей альтернативы). После некоторой дополнительной обработки он вызывает DispatchMessage(), который отправляет сообщение соответствующему обработчику, также известному как WindowProc . Обычно сообщения, у которых нет специального WindowProc(), отправляются в DefWindowProc , который используется по умолчанию. DispatchMessage() вызывает WindowProc дескриптора HWND сообщения (зарегистрированного с помощью функции RegisterClass()).

Порядок сообщений

Более поздние версии Microsoft Windows гарантируют программисту, что сообщения будут доставлены в цикл сообщений приложения в том порядке, в котором они были восприняты системой и ее периферийными устройствами. Эта гарантия важна при рассмотрении последствий проектирования многопоточных приложений.

Однако для некоторых сообщений действуют другие правила, например сообщения, которые всегда принимаются последними, или сообщения с другим документированным приоритетом. [4]

X оконная система

Цикл событий 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 абстрагируется в Cocoa как NSRunLoop, который позволяет любому сообщению (эквивалентному вызову функции в неотражающих средах выполнения) ставиться в очередь для отправки любому объекту.

Смотрите также

Рекомендации

  1. ^ "Linux_2_6_16 - Новички в ядре Linux" . kernelnewbies.org . Проверено 03 марта 2021 г.
  2. ^ DJ Бернштейн. «Трюк с самодельной трубкой».
  3. ^ ОШИБКИ,: pselect(2)синхронное мультиплексирование ввода-вывода -  Руководство программиста Linux - Системные вызовы
  4. ^ Функция GetMessage() со списком приоритетов сообщений.

Внешние ссылки