Połączenie, komunikacja. Synchroniczna. Asynchroniczna. Wielowątkowość. Multithreading. Być może, któreś z tych słów już miałeś okazję usłyszeć. W dzisiejszym artykule przyjrzymy się im z bliska i rozwiejemy watpliwości.
Jeden wątek
Wyobraź sobie człowieka, który w danym momencie w czasie jest w stanie zająć się tylko jedną rzeczą. Postępuje zadaniowo. Wkłada naczynia do zmywarki. Jedzie na zakupy i stojąc w kolejce do kasy zajęty jest czekaniem na swoją kolej. Wysyła maila, aby pozyskać informacje i czekając na odpowiedź nie robi nic innego dopóki nie uzyska odpowiedzi i nie zamknie swojego zapytania. Jednym słowem działa jednowątkowo.
W podobny sposób może działać aplikacja. Wyobraź sobie, że wprowadzasz parametry do formularza, na podstawie których mają być odfiltrowane i wyrenderowane na diagramie wyniki. Przetwarzanie danych trwa, aplikacja więc na kilkadziesiąt sekund zawiesza się („freezuje się”) i użytkownik ma wrażenie, że coś poszło nie tak. Tymczasem wszystko jest w jak najlepszym porządku, a aplikacja po prostu czeka. W końcu otrzymuje odpowiedź z serwera, dane są przetworzone i gotowe do wyrenderowania (zwizualizowania) po stronie użytkownika. Działa jednowątkowo, a komunikacja przebiega synchronicznie. Czy byłbyś zadowolony z takiego obrotu spraw?
Wiele watków
Ja jako użytkownik z pewnością nie. Konieczne jest więc rozwiązanie tego w inny sposób. Chcemy, aby komunikacja przebiegała asynchronicznie. Jako aplikacja kliencka chcemy po prostu przekazać parametry i zgłosić serwerowi zapotrzebowanie na przetworzenie danych. Pragniemy otrzymać tak przygotowane dane, aby można je było zwizualizować na wykresie po stronie frontendu. Jednocześnie w międzyczasie nie chcemy czekać. Zamrożenie aplikacji (równoznaczne z niemożliwością nawiązania z nia jakiejkolwiek interakcji) jest niedopuszczalne. Użytkownik powinien móc normalnie z niej korzystać, ewentualnie widzieć content loader/spinner sygnalizujący oczekiwanie. Gdy nadejdzie odpowiedź z backendu możemy na nią zareagować i powrócić do rozpoczętej przez użytkownika akcji – zaprezentować mu wykres z odfiltrowanymi danymi.
Jak osiągnąć taki stan? Przede wszystkim korzystając z wielu wątków, stosując multithreading. Wówczas wątek główny aplikacji nie jest zablokowany czekaniem na odpowiedź z serwera. Może w dalszym ciągu obsługiwać żądania użytkownika, być responsywny. Zapytanie, które wymaga dłuższego oczekiwania na odpowiedź jest wykonywane na innym wątku.
Synchroniczność i asynchroniczność w świecie aplikacji
Synchroniczność moglibyśmy również porównać do rozmowy z konsultantem banku na komunikatorze chatu w czasie rzeczywistym. Wysyłamy wiadomość i oczekujemy na odpowiedź. W trakcie, kiedy konsultant lub (coraz częściej) bot komponuje dla nas odpowiedź jesteśmy nadal w procesie w 100% obecni – czekamy.
Aplikacje działając, generują wiadomości, wysyłają żądania do API i mikroserwisów, wywołują funkcje. Przy komunikacji synchronicznej po wysłaniu takiego żądania software pozostawałby w stanie bezczynności. Kod nie wykonywałby się dalej i trwałoby to do momentu otrzymania odpowiedzi na wysłane żądanie.
W przeciwieństwie do tego przy komunikacji asynchronicznej wysłanie żądania nie powoduje zawieszenia wykonywania kodu źródłowego. Aplikacja działa dalej. Przykładem działania asynchronicznego będzie wbudowany firmware drukarki, który potrafi wysłać komunikat o niskim poziomie toneru jednocześnie nie przerywając drukowania.
Podsumowanie
Reasumując mamy dwa rodzaje komunikacji – synchroniczną i asynchroniczną. Zdając sobie sprawę, że skojarzenie ich z jedno- oraz wielowątkowością może nie być oczywiste, przygotowałam dla Ciebie następujace podsumowanie:
Komunikacja synchroniczna = jednowątkowa ( ang. single thread ):
- (+) Pozwala na przetwarzanie w czasie rzeczywistym
- (+) Zazwyczaj łatwiejsza do śledzenia potencjalnych błędów
- (-) Bywa czasochłonna
- (-) Prowadzi do okresów bezczynności w momencie oczekiwania na wykonanie zadanej czynności/akcji
- (-) Doświadczenia użytkownika mogą być negatywne
Komunikacja asynchroniczna = wielowątkowa ( ang. multithreading ):
- (+) Działanie aplikacji nie ejst blokowane przez wysłane żądania i inne wywołania czekające na swoją kolej
- (+) Wiele czynności może być wykonanych w jednym momencie, co obniża czas oczekiwania użytkowników
- (-) Czasy odpowiedzi nie zawsze są przewidywalne, nie mamy gwarancji tego, że „nie przejdziemy dalej dopóki nie skończymy”
- (-) Trudniejsze śledzenie ewentualnych błędów
Oczywiście zupełnie niezależnie od wiedzy ze świata IT, toczą sie dywagacje na temat tego, w jakim trybie lepiej działa człowiek. Multitasking i przełączanie wątków nie wydają się dobrze służyć naszej produktywności. Zastanówcie się, jak na co dzień Wy działacie- jedno- czy raczej wielowątkowo? 😉