Prosta odpowiedź brzmi: nie wszystkie wątki wykonują się jednocześnie. Aby uzyskać pełniejsze wyjaśnienie, czytaj dalej.
Generalnie uważa się, że harmonogram zadań systemu operacyjnego służy do planowania aplikacji, dzięki czemu można wykonać jedno zadanie, gdy komputer pracuje nad innym. W dawnych czasach lakmusowym testem wielozadaniowości było formatowanie dyskietki podczas robienia czegoś innego. Jeśli naprawdę chcesz przetestować system operacyjny, sformatuj dyskietkę podczas pobierania pliku przez modem podłączony do portu szeregowego. Ponieważ sprzęt stał się wystarczająco potężny, aby zrobić to w znaczący sposób, odtwarzanie wideo czasami występowało również w takich testach. Jeśli harmonogram zadań systemu operacyjnego może bezproblemowo obsłużyć te zadania, może obsłużyć wszystko.
Jednak harmonogram zadań w rzeczywistości nie planuje aplikacji (procesów), planuje wątki . Każda aplikacja ma co najmniej jeden wątek, ale może potencjalnie użyć dużej liczby wątków, aby podzielić pracę, którą wykonuje, na powiązane lub niezależne części. Na przykład aplikacja często ma jeden wątek, który obsługuje interfejs użytkownika, i tworzy inny wątek, gdy użytkownik zainicjuje potencjalnie długotrwałą operację (która może polegać na drukowaniu, ponownym obliczaniu arkusza kalkulacyjnego lub działaniu środowiska programistycznego wyszukiwanie symboli itp.). Niektóre środowiska programistyczne wprowadzają pewną liczbę wątków niewidocznie dla programisty; na przykład Java i .NET mogą wykonywać wyrzucanie elementów bezużytecznychw osobnym wątku, który jest poza bezpośrednią kontrolą programisty. Niektóre programy tworzą wiele wątków wcześnie i łączą je, ponieważ tworzenie nowych wątków jest stosunkowo kosztowną operacją (więc niekoniecznie trzeba tworzyć wątek za każdym razem, gdy jest potrzebny). Wszystko, co wykonuje podgląd, jest zwykle wykonywane w osobnym wątku, więc reszta interfejsu użytkownika pozostaje responsywna podczas generowania podglądu. I tak dalej. Podsumowując, wszystko to oznacza, że liczba wątków w systemie w dowolnym momencie może łatwo być wielokrotnością liczby procesów.
Każdy wątek może znajdować się w jednym z kilku możliwych stanów, ale najważniejsze jest rozróżnienie między stanami pracy , uruchamiania i oczekiwania ; terminologia może się nieco różnić, ale taka jest ogólna idea. W każdej chwili, tylko jeden wątek na wirtualnym (ze względu HyperThreading i podobnych technologii) rdzenia procesora może być uruchomiony (czyli wykonywanie instrukcji kodu maszyna), ale dowolną liczbę wątków może być runnable (co oznacza, że jest to kandydat, aby uzyskać CPU następnym razem, gdy program planujący musi podjąć decyzję, który wątek powinien zostać uruchomiony). Czekanie (zwane także blokowanymi) wątkami są po prostu tym, że na coś czekają - najczęstsze przypadki to prawdopodobnie oczekiwanie na użytkownika, dysk lub sieciowe we / wy (w szczególności wprowadzanie danych przez użytkownika jest wyjątkowo wolne).
Liczba wątków widoczna w menedżerze zadań to całkowita liczba wątków w dowolnym z tych stanów. Na przykład system Windows 7, na którym piszę, ma obecnie uruchomionych około 70 procesów, ale prawie 900 wątków. Biorąc pod uwagę wszystkie procesy w tle do obsługi różnych zadań i sposób, w jaki są one prawdopodobnie podzielone na wiele wątków, nie jest to oburzająca liczba.
Wchodząc nieco głębiej w techniczne wdrażanie, istotą harmonogramu zadań systemu operacyjnego z wyprzedzeniem wielozadaniowości jest zazwyczaj pewnego rodzaju hak przerwania sprzętowego. Oznacza to, że jądro może powstrzymać CPU gdy nie ma użytecznej pracy do wykonania (jest to prawie na pewno jednym z powodów, jeżeli nie powód, dlaczego Linux sprawdza się instrukcji, na bagażniku na IA-32HLT
-kompatybilne procesory i prawdopodobnie wykonuje podobne kontrole na innych architekturach), wiedząc, że w pewnym rozsądnym terminie w przyszłości zostanie przerwane i zostanie uruchomione harmonogram zadań. Ponieważ przerwanie jest uruchamiane bez względu na to, jaką inną pracę wykonuje procesor (taka jest idea przerwania), harmonogram jest wykonywany regularnie i ma szansę określić, który wątek powinien zostać wykonany w następnym przedziale czasowym. Ponieważ przełączniki kontekstu są stosunkowo drogie, zazwyczaj możliwe jest (przynajmniej przez kod źródłowy) dostrojenie, jak agresywnie program planujący przełącza się między wątkami; przełączanie wątków częściej powoduje, że system staje się bardziej responsywny, ale narzut przełączania oznacza, że całkowity czas do ukończenia danego zestawu zadań jest dłuższy. najszybciejsystem będzie przełączał się między wątkami tylko wtedy, gdy działający wątek nie jest już możliwy do uruchomienia (co oznacza, że blokuje się oczekiwanie na coś lub zakończył pracę), ponieważ minimalizuje to obciążenie, podczas gdy najbardziej responsywny system przełącza się między wątkami za każdym razem, gdy wywoływany jest program planujący, ponieważ minimalizuje to średni czas oczekiwania, zanim określony wątek uzyska czas procesora. Idealne ustawienie zwykle znajduje się gdzieś pomiędzy tymi dwoma, a kompromis między tymi wyborami jest prawdopodobnie jednym z głównych powodów, dla których Linux oferuje wiele harmonogramów do wyboru, a także niektóre parametry strojenia poprzez konfigurację jądra.
Z drugiej strony systemy operacyjne i środowiska współpracujące z wieloma zadaniami ( Windows 3.x jest jednym z przykładów), polegają na każdej aplikacji, aby regularnie przekazywać kontrolę harmonogramowi. Zwykle jest to funkcja API specjalnie przeznaczona do tego, a często wiele funkcji API robi to w ramach wewnętrznego przepływu wykonywania, ponieważ pomaga to usprawnić działanie użytkownika. To podejście projektowe działa dobrze, o ile wszystkie aplikacje są dobrze zachowane i kontrolują ced w krótkich odstępach czasu podczas dowolnych długotrwałych operacji (długo działający oznacza więcej niż ułamek sekundy), ale aplikacja, która nie może się zapchać cały system. Jest to jeden z głównych powodów, dla których system Windows 3.x wypadł tak słabo w teście wielozadaniowości, o którym wspomniałem powyżej, podczas gdy OS / 2spacerował wesoło, wykonując te same zadania na tym samym sprzęcie: aplikacja mogła powiedzieć stacji dyskietek, aby napisała określony sektor, a czas, jaki upłynął, zanim zwróciła wywołanie, mógł być rzeczywiście mierzalny (dziesiątki do setek milisekund lub więcej); zapobiegawczo wielozadaniowy system miałby włamać się do swojego harmonogramu przy następnym zaplanowanym wywołaniu, zauważyć, że wątek, który jest obecnie „uruchomiony”, jest faktycznie blokowany przez wywołanie zapisu i po prostu przełącza się na inny wątek, który można uruchomić. (W praktyce jest to trochę bardziej zaangażowane, ale taki jest ogólny pomysł).
W środowiskach zapobiegawczych wielozadaniowych i kooperacyjnych istnieje również możliwość, że różne wątki będą miały różne priorytety. Na przykład prawdopodobnie ważniejsze jest wykonanie w odpowiednim czasie wątku, który odbiera dane przez łącze komunikacyjne, niż ten, który aktualizuje wyświetlanie czasu systemowego, więc wątek odbierający ma wysoki priorytet, a wątek aktualizujący wyświetlanie czasu ma niski priorytet . Priorytety wątków odgrywają rolę w podejmowaniu decyzji przez program planujący, który wątek umożliwia wykonanie (na przykład bardzo uproszczony, wątki o wysokim priorytecie powinny zawsze być wykonywane przed wątkami o niskim priorytecie, więc nawet jeśli wątek o niskim priorytecie ma jeszcze do zrobienia, jeśli wątek o wysokim priorytecie staje się uruchamialny, ma pierwszeństwo), ale takie konkretne decyzje dotyczące planowania nie wpływają na projekt mechanizmu leżącego u podstaw.