Pętla zdarzeń - Event loop

W informatyce , pętla zdarzenie jest programowanie konstrukt lub wzorzec projektowy , który czeka i wywołuje zdarzenia lub komunikaty w programie . Pętla zdarzeń działa, wysyłając żądanie do wewnętrznego lub zewnętrznego „dostawcy zdarzeń” (który zazwyczaj blokuje żądanie do momentu nadejścia zdarzenia), a następnie wywołuje odpowiednią procedurę obsługi zdarzeń („wysyła zdarzenie”). Pętla zdarzenie jest również czasami określane jako wysyłający wiadomości , pętli komunikatów , pompy wiadomości lub pętli biegu .

Pętla zdarzeń może być używana w połączeniu z reaktorem , jeśli dostawca zdarzenia podąża za interfejsem pliku , który można wybrać lub "odpytać" (wywołanie systemowe Unix, a nie rzeczywiste odpytywanie ). Pętla zdarzeń prawie zawsze działa asynchronicznie z nadawcą komunikatu.

Gdy pętla zdarzeń tworzy konstrukcję centralnego przepływu sterowania programu, jak to często ma miejsce, można ją nazwać główną pętlą lub główną pętlą zdarzeń . Ten tytuł jest odpowiedni, ponieważ taka pętla zdarzeń jest na najwyższym poziomie kontroli w programie.

Przekazywanie wiadomości

Mówi się, że pompy komunikatów „pompują” komunikaty z kolejki komunikatów programu (przypisane i zwykle należące do bazowego systemu operacyjnego) do programu w celu przetworzenia. W najściślejszym sensie pętla zdarzeń jest jedną z metod implementacji komunikacji między procesami . W rzeczywistości przetwarzanie komunikatów istnieje w wielu systemach, w tym w składniku systemu operacyjnego Mach na poziomie jądra . Pętla zdarzeń to specyficzna technika implementacji systemów wykorzystujących przekazywanie komunikatów .

Alternatywne projekty

To podejście różni się od wielu innych alternatyw:

  • Tradycyjnie program uruchamiał się tylko raz, a następnie kończył. Ten typ programu był bardzo popularny we wczesnych dniach informatyki i brakowało mu jakiejkolwiek formy interakcji z użytkownikiem. Jest to nadal często używane, szczególnie w postaci programów uruchamianych z wiersza poleceń . Wszelkie parametry są ustawiane z wyprzedzeniem i przekazywane za jednym razem po uruchomieniu programu.
  • Projekty oparte na menu. Mogą one nadal zawierać główną pętlę, ale zwykle nie są traktowane jako zdarzenia sterowane w zwykłym sensie. Zamiast tego użytkownik otrzymuje coraz zawężający się zestaw opcji, aż zadanie, które chce wykonać, jest jedyną dostępną opcją. Dostępna jest ograniczona interaktywność poprzez menu.

Stosowanie

Ze względu na przewagę graficznych interfejsów użytkownika większość nowoczesnych aplikacji posiada pętlę główną. Procedura get_next_message()jest zazwyczaj dostarczana przez system operacyjny i blokuje się do momentu, gdy pojawi się komunikat. W ten sposób pętla jest wprowadzana tylko wtedy, gdy jest coś do przetworzenia.

function main
    initialize()
    while message != quit
        message := get_next_message()
        process_message(message)
    end while
end function

Interfejs plików

W systemie Unix paradygmat " wszystko jest plikiem " w naturalny sposób prowadzi do pętli zdarzeń opartej na plikach. Odczytywanie i zapisywanie do plików, komunikacja między procesami, komunikacja sieciowa i sterowanie urządzeniami są osiągane przy użyciu operacji wejścia/wyjścia pliku, przy czym cel jest identyfikowany przez deskryptor pliku . Do wybierz i poll wywołania systemowe pozwalają zestaw deskryptorów być monitorowane za pomocą zmiany stanu, na przykład gdy dane będą dostępne do odczytu.

Rozważmy na przykład program, który czyta z ciągle aktualizowanego pliku i wyświetla jego zawartość w X Window System , który komunikuje się z klientami przez gniazdo ( domenę Unix lub 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()

Obsługa sygnałów

Jedną z niewielu rzeczy w Unixie, które nie są zgodne z interfejsem plików, są zdarzenia asynchroniczne ( sygnały ). Sygnały są odbierane w programach obsługi sygnałów , małych, ograniczonych fragmentach kodu, które działają, gdy reszta zadania jest zawieszona; jeśli sygnał zostanie odebrany i obsłużony podczas blokowania zadania select(), select wróci wcześniej z EINTR ; jeśli sygnał zostanie odebrany, gdy zadanie jest związane z procesorem , zadanie zostanie zawieszone między instrukcjami do czasu powrotu funkcji obsługi sygnału.

Tak więc oczywistym sposobem obsługi sygnałów jest ustawienie przez programy obsługi sygnałów globalnej flagi i sprawdzenie pętli zdarzeń bezpośrednio przed i po select()wywołaniu; jeśli jest ustawiona, obsłuż sygnał w taki sam sposób, jak w przypadku zdarzeń na deskryptorach plików. Niestety, prowadzi to do sytuacji wyścigu : jeśli sygnał nadejdzie natychmiast między sprawdzeniem flagi a wywołaniem select(), nie zostanie on obsłużony, dopóki nie select()powróci z jakiegoś innego powodu (na przykład przerwanie przez sfrustrowanego użytkownika).

Rozwiązaniem POSIX jest pselect()wywołanie, które jest podobne do tego, select()ale przyjmuje dodatkowy sigmaskparametr, który opisuje maskę sygnału . Pozwala to aplikacji na maskowanie sygnałów w głównym zadaniu, a następnie usunięcie maski na czas trwania select()wywołania, tak aby programy obsługi sygnałów były wywoływane tylko wtedy, gdy aplikacja jest powiązana we/wy . Jednak implementacje pselect()nie zawsze były niezawodne; wersje Linuksa wcześniejsze niż 2.6.16 nie mają pselect()wywołania systemowego, zmuszając glibc do emulowania go za pomocą metody podatnej na ten sam wyścig, którego pselect()należy unikać.

Alternatywnym, bardziej przenośnym rozwiązaniem jest konwersja zdarzeń asynchronicznych na zdarzenia oparte na plikach za pomocą sztuczki z potokami własnymi , w której „obsługa sygnału zapisuje bajt do potoku, którego drugi koniec jest monitorowany select()w programie głównym”. W jądrze Linux w wersji 2.6.22 dodano nowe wywołanie systemowe signalfd(), które umożliwia odbieranie sygnałów poprzez specjalny deskryptor pliku.

Realizacje

Aplikacje Windows

W systemie operacyjnym Microsoft Windows proces, który wchodzi w interakcję z użytkownikiem, musi akceptować i reagować na wiadomości przychodzące, co prawie nieuchronnie jest wykonywane przez pętlę wiadomości w tym procesie. W systemie Windows wiadomość jest równoważna zdarzeniu utworzonemu i narzuconemu systemowi operacyjnemu. Zdarzeniem może być między innymi interakcja użytkownika, ruch sieciowy, przetwarzanie systemu, aktywność zegara, komunikacja między procesami. W przypadku nieinteraktywnych zdarzeń tylko we/wy system Windows ma porty zakończenia we/wy . Pętle portu zakończenia we/wy działają niezależnie od pętli wiadomości i nie wchodzą w interakcję z pętlą wiadomości po wyjęciu z pudełka.

„Sercem” większości aplikacji Win32 jest funkcja WinMain() , która wywołuje funkcję GetMessage() w pętli. GetMessage() blokuje do momentu odebrania komunikatu lub „zdarzenia” (z funkcją PeekMessage() jako alternatywą nieblokującą). Po pewnym opcjonalnym przetworzeniu wywoła DispatchMessage() , co spowoduje wysłanie komunikatu do odpowiedniego programu obsługi, znanego również jako WindowProc . Normalnie komunikaty, które nie mają specjalnego WindowProc(), są wysyłane do domyślnej DefWindowProc . DispatchMessage() wywołuje WindowProc uchwytu HWND komunikatu (zarejestrowanego za pomocą funkcji RegisterClass() ).

Zamawianie wiadomości

Nowsze wersje systemu Microsoft Windows gwarantują programiście, że komunikaty będą dostarczane do pętli komunikatów aplikacji w kolejności, w jakiej były postrzegane przez system i jego urządzenia peryferyjne. Ta gwarancja jest niezbędna przy rozważaniu konsekwencji projektowych aplikacji wielowątkowych .

Jednak niektóre wiadomości mają inne reguły, na przykład wiadomości, które są zawsze odbierane jako ostatnie lub wiadomości o innym udokumentowanym priorytecie.

X Window System

Pętla zdarzeń Xlib

Aplikacje X korzystające bezpośrednio z Xlib są zbudowane wokół XNextEventrodziny funkcji; XNextEventblokuje się do momentu pojawienia się zdarzenia w kolejce zdarzeń, po czym aplikacja odpowiednio je przetworzy. Pętla zdarzeń Xlib obsługuje tylko zdarzenia systemu okienkowego; aplikacje, które muszą być w stanie czekać na inne pliki i urządzenia, mogą skonstruować własną pętlę zdarzeń z prymitywów takich jak ConnectionNumber, ale w praktyce zwykle używają wielowątkowości .

Bardzo niewiele programów korzysta bezpośrednio z Xlib. W bardziej powszechnym przypadku zestawy narzędzi GUI oparte na Xlib zwykle obsługują dodawanie zdarzeń. Na przykład zestawy narzędzi oparte na Xt Intrinsics mają XtAppAddInput()i XtAppAddTimeout().

Proszę zauważyć, że nie jest bezpieczne wywoływanie funkcji Xlib z programu obsługi sygnału, ponieważ aplikacja X mogła zostać przerwana w dowolnym stanie, np XNextEvent. w obrębie . Zobacz [1] rozwiązanie dla X11R5, X11R6 i Xt.

Pętla zdarzeń GLib

GLib pętla zdarzenie zostało pierwotnie stworzone do użytku w GTK , ale jest teraz używany w aplikacjach innych niż GUI, jak również, takie jak D-Bus . Odpytywany zasób to zbiór deskryptorów plików, którymi interesuje się aplikacja; blok odpytywania zostanie przerwany, jeśli nadejdzie sygnał lub upłynie limit czasu (np. jeśli aplikacja określiła limit czasu lub zadanie bezczynności). Chociaż biblioteka GLib ma wbudowaną obsługę deskryptorów plików i zdarzeń zakończenia podrzędnego, możliwe jest dodanie źródła zdarzenia dla dowolnego zdarzenia, które można obsłużyć w modelu przygotowania-sprawdzenia-wysyłania. [2]

Biblioteki aplikacji, które są zbudowane na pętli zdarzeń GLib, obejmują GStreamer i asynchroniczne metody we/wy GnomeVFS , ale GTK pozostaje najbardziej widoczną biblioteką klienta. Zdarzenia z systemu okienkowego (w X , odczytane z gniazda X ) są tłumaczone przez GDK na zdarzenia GTK i emitowane jako sygnały GLib na obiektach widgetów aplikacji.

Pętle uruchamiania systemu macOS Core Foundation

Dozwolony jest dokładnie jeden CFRunLoop na wątek i można dołączyć dowolnie wiele źródeł i obserwatorów. Źródła komunikują się następnie z obserwatorami poprzez pętlę uruchamiania, organizując kolejkowanie i wysyłanie wiadomości.

CFRunLoop są wydobywane w kakao jako NSRunLoop pozwalającego na wiadomość (odpowiada to wywołanie funkcji spoza odblaskowe czasy pracy) w kolejce do wysyłania do dowolnego obiektu.

Zobacz też

Bibliografia

Zewnętrzne linki