W jaki sposób można osiągnąć funkcjonalność wielowątkową w języku wysokiego poziomu, takim jak Java, używając tylko jednego wątku i automatu stanów? Na przykład co zrobić, jeśli do wykonania są 2 działania (wykonywanie obliczeń i wykonywanie operacji we / wy) i jedno działanie może zostać zablokowane?
To, co opisujesz, nazywa się współpracującym wielozadaniowością , w którym zadania otrzymują procesor i oczekuje się, że zrzeknie się go dobrowolnie po upływie określonego czasu lub aktywności. Zadanie, które nie współpracuje przez dalsze korzystanie z procesora lub blokowanie gumy całej pracy i brak sprzętowego timera nadzorującego, nie ma nic, co kod nadzorujący zadania może z tym zrobić.
To, co widzisz w nowoczesnych systemach, nazywa się zapobiegawczym wielozadaniowością , czyli tam, gdzie zadania nie muszą rezygnować z procesora, ponieważ superwizor robi to za nich, gdy nadejdzie przerwanie generowane przez sprzęt. Procedura usługi przerwania w superwizorze zapisuje stan procesora i przywraca go następnym razem, gdy zadanie zostanie uznane za zasługujące na wycinek czasu, a następnie przywraca stan z dowolnego zadania, które ma zostać wykonane, i wskakuje z powrotem, jakby nic się nie wydarzyło . To działanie nazywa się przełączaniem kontekstu i może być kosztowne.
Czy używanie „tylko maszyny stanów” jest realną alternatywą dla wielowątkowości w językach wysokiego poziomu?
Wykonalny? Pewnie. Rozsądny? Czasami. To, czy użyjesz nici, czy jakiejś formy domowej wielozadaniowej współpracy (np. Maszyny stanowe), zależy od kompromisów, które chcesz zrobić.
Wątki upraszczają projektowanie zadań do tego stopnia, że można traktować każdy z nich jak własny program, który dzieli się przestrzenią danych z innymi. Daje to swobodę skupienia się na wykonywanym zadaniu, a nie na całym zarządzaniu i pracach domowych wymaganych do wykonania iteracji na raz. Ale ponieważ żaden dobry uczynek nie pozostaje bezkarny, płacisz za całą tę wygodę w przełącznikach kontekstowych. Posiadanie wielu wątków, które dają procesorowi minimalną pracę (dobrowolnie lub wykonując coś, co blokuje, takie jak operacje we / wy), może pochłonąć dużo czasu procesora podczas przełączania kontekstu. Jest to szczególnie ważne, jeśli operacje blokowania rzadko blokują się bardzo długo.
Istnieją sytuacje, w których trasa kooperacyjna ma większy sens. Kiedyś musiałem napisać oprogramowanie dla użytkownika dla sprzętu, który przesyłał strumieniowo wiele kanałów danych przez interfejs odwzorowany w pamięci, który wymagał odpytywania. Każdy kanał był obiektem zbudowanym w taki sposób, że mogłem pozwolić mu działać jako wątek lub wielokrotnie wykonywać pojedynczy cykl odpytywania.
Wydajność wersji wielowątkowej wcale nie była dobra z dokładnie tego powodu, który opisałem powyżej: każdy wątek wykonywał minimalną pracę, a następnie produkował procesor, więc inne kanały mogły mieć trochę czasu, powodując wiele przełączeń kontekstu. Zezwolenie na uruchamianie wątków do momentu wstrzymania pomogło w zwiększeniu przepustowości, ale spowodowało, że niektóre kanały nie były obsługiwane, zanim sprzęt nie przepełni bufora, ponieważ nie dostali wystarczająco dużo czasu.
Wersja jednowątkowa, która wykonywała nawet iteracje każdego kanału, działała jak oparzona małpa, a obciążenie systemu spadało jak kamień. Karą, którą zapłaciłem za dodatkowy występ, było to, że sam musiałem żonglować zadaniami. W tym przypadku kod do zrobienia tego był na tyle prosty, że koszt jego opracowania i utrzymania był wart poprawy wydajności. Myślę, że to naprawdę podstawa. Gdyby moje wątki były tymi, które siedziały i czekały na jakieś wywołanie systemowe, to ćwiczenie prawdopodobnie nie byłoby tego warte.
To prowadzi mnie do komentarza Coxa: wątki nie są przeznaczone wyłącznie dla osób, które nie potrafią pisać automatów państwowych. Niektórzy ludzie są w stanie to zrobić, ale decydują się na użycie puszkowanej maszyny stanów (tj. Wątku) w celu wykonania pracy wcześniej lub z mniejszą złożonością.