stringtranslate.com

Цикл событий

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

Он также широко применяется на таких серверах, как веб-серверы .

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

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

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

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

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

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

Использование

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

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

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

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

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

def  main ():  file_fd  =  open ( "logfile.log" )  x_fd  =  open_display ( )  construct_interface ()  while  True :  rlist ,  _ ,  _  =  select.select ([ file_fd , x_fd ], [], []): if file_fd in rlist : data = file_fd.read ( ) append_to_display ( data ) send_repaint_message ( ) if x_fd in 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()призвано избежать этого.

Альтернативным, более портативным решением является преобразование асинхронных событий в файловые события с помощью трюка self-pipe , [2] где «обработчик сигнала записывает байт в канал, другой конец которого отслеживается select()в основной программе». [3] В ядре Linux версии 2.6.22 был добавлен новый системный вызов signalfd(), который позволяет получать сигналы через специальный файловый дескриптор.

Реализации

HTML/JavaScript

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

Приложения Windows

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

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

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

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

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

X-Window-система

Цикл событий Xlib

Приложения X , напрямую использующие Xlib , построены вокруг XNextEventсемейства функций; XNextEventблокирует до тех пор, пока событие не появится в очереди событий, после чего приложение обрабатывает его соответствующим образом. Цикл событий Xlib обрабатывает только системные события окна; приложения, которым необходимо иметь возможность ждать другие файлы и устройства, могут построить свой собственный цикл событий из примитивов, таких как ConnectionNumber, но на практике, как правило, используют многопоточность .

Очень немногие программы используют Xlib напрямую. В более распространенном случае наборы инструментов GUI на основе Xlib обычно поддерживают добавление событий. Например, наборы инструментов на основе Xt Intrinsics имеют XtAppAddInput()и XtAppAddTimeout().

Обратите внимание, что вызывать функции Xlib из обработчика сигналов небезопасно, поскольку приложение X могло быть прервано в произвольном состоянии, например, в XNextEvent. См. [1] для решения для X11R5, X11R6 и Xt.

Цикл событий GLib

Цикл событий GLib изначально был создан для использования в GTK , но теперь используется и в не-GUI-приложениях, таких как 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 Kernel Newbies". kernelnewbies.org . Получено 2021-03-03 .
  2. ^ DJ Bernstein. «Трюк с самостоятельным звучанием».
  3. ^ ОШИБКИ, pselect(2): синхронное мультиплексирование ввода-вывода –  Руководство программиста Linux – Системные вызовы
  4. ^ Функция GetMessage() со списком приоритетов сообщений.

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