Pomyślałem, że celem komputera wielordzeniowego jest to, że może uruchamiać wiele wątków jednocześnie. W takim przypadku, jeśli masz maszynę czterordzeniową, jaki jest sens jednoczesnego uruchamiania więcej niż 4 wątków? Czy nie kradną sobie po prostu czasu (zasobów procesora)?
multithreading
hardware
cpu-cores
Nick Heiner
źródło
źródło
Odpowiedzi:
Odpowiedź obraca się wokół celu wątków, którym jest równoległość: uruchomienie kilku oddzielnych linii wykonania jednocześnie. W „idealnym” systemie miałbyś wykonywać jeden wątek na rdzeń: bez przerw. W rzeczywistości tak nie jest. Nawet jeśli masz cztery rdzenie i cztery wątki robocze, Twój proces i jego wątki będą stale przełączane na inne procesy i wątki. Jeśli używasz dowolnego nowoczesnego systemu operacyjnego, każdy proces ma co najmniej jeden wątek, a wiele ma więcej. Wszystkie te procesy działają jednocześnie. Prawdopodobnie masz teraz kilkaset wątków uruchomionych na komputerze. Nigdy nie dojdzie do sytuacji, w której wątek będzie działał bez „kradzieży” czasu. (Cóż, możesz, jeśli działa w czasie rzeczywistym, jeśli używasz systemu operacyjnego czasu rzeczywistego lub nawet w systemie Windows użyj priorytetu wątków w czasie rzeczywistym. Ale to rzadkie.)
Biorąc to pod uwagę jako tło, odpowiedź: tak, więcej niż cztery wątki na prawdziwej czterordzeniowej maszynie mogą spowodować sytuację, w której „kradną sobie czas”, ale tylko wtedy, gdy każdy pojedynczy wątek potrzebuje 100% procesora . Jeśli wątek nie działa w 100% (ponieważ wątek interfejsu użytkownika może nie działać lub wątek wykonujący niewielką ilość pracy lub czekający na coś innego), to inny zaplanowany wątek jest w rzeczywistości dobrą sytuacją.
W rzeczywistości jest to bardziej skomplikowane:
A co, jeśli masz pięć prac, które trzeba wykonać na raz? Bardziej sensowne jest uruchomienie ich wszystkich naraz, niż uruchomienie czterech z nich, a piątego później.
Rzadko się zdarza, aby wątek naprawdę potrzebował 100% procesora. Na przykład w momencie, gdy używa dysku lub sieci we / wy, może potencjalnie spędzać czas na czekaniu, nie robiąc nic pożytecznego. To bardzo powszechna sytuacja.
Jeśli masz zadanie do wykonania, jednym z powszechnych mechanizmów jest użycie puli wątków. Wydawałoby się, że sensowne jest posiadanie takiej samej liczby wątków co rdzeni, jednak pula wątków .Net ma do 250 wątków dostępnych na procesor . Nie jestem pewien, dlaczego to robią, ale przypuszczam, że ma to związek z rozmiarem zadań, które mają być uruchamiane w wątkach.
Tak więc: kradzież czasu nie jest złą rzeczą (i tak naprawdę nie jest też kradzieżą: tak ma działać system). Pisz swoje programy wielowątkowe w oparciu o rodzaj pracy, którą wykonają wątki, a może to nie być procesor -uwiązany. Określ liczbę potrzebnych wątków na podstawie profilowania i pomiarów. Może się okazać, że bardziej przydatne będzie myślenie w kategoriach zadań lub zadań niż wątków: pisz obiekty pracy i przekazuj je do puli do uruchomienia. Wreszcie, jeśli Twój program nie jest naprawdę krytyczny dla wydajności, nie martw się zbytnio :)
źródło
To, że wątek istnieje, nie zawsze oznacza, że jest aktywnie uruchomiony. Wiele zastosowań wątków obejmuje niektóre wątki, które przechodzą w stan uśpienia, dopóki nie nadejdzie czas, aby coś zrobiły - na przykład dane wejściowe użytkownika wyzwalają wątki, aby się obudzić, wykonać pewne przetwarzanie i wrócić do snu.
Zasadniczo wątki to indywidualne zadania, które mogą działać niezależnie od siebie, bez konieczności zdawania sobie sprawy z postępu innego zadania. Jest całkiem możliwe, że masz ich więcej, niż masz możliwości uruchamiania jednocześnie; nadal są przydatne dla wygody, nawet jeśli czasami muszą stać w kolejce jeden za drugim.
źródło
Chodzi o to, że pomimo braku rzeczywistego przyspieszenia, gdy liczba wątków przekracza liczbę rdzeni, można użyć wątków do rozdzielenia elementów logiki, które nie powinny być od siebie zależne.
Nawet w średnio złożonej aplikacji, używając pojedynczego wątku, spróbuj zrobić wszystko szybko, tworząc skrót z „przepływu” kodu. Pojedynczy wątek spędza większość czasu na sprawdzaniu tego, sprawdzaniu tego, warunkowym wywoływaniu procedur w razie potrzeby, i trudno jest dostrzec cokolwiek poza grzęzawiskiem drobiazgów.
Porównaj to z przypadkiem, w którym możesz poświęcić wątki na zadania, aby patrząc na dowolny pojedynczy wątek, zobaczyć, co robi ten wątek. Na przykład jeden wątek może blokować oczekiwanie na dane wejściowe z gniazda, analizować strumień na komunikaty, filtrować komunikaty, a gdy pojawi się poprawny komunikat, przekazywać go do innego wątku roboczego. Wątek roboczy może pracować na danych wejściowych z wielu innych źródeł. Kod każdego z nich będzie wykazywał czysty, celowy przepływ, bez konieczności jawnego sprawdzania, czy nie ma nic innego do zrobienia.
Partycjonowanie pracy w ten sposób pozwala aplikacji polegać na systemie operacyjnym, aby zaplanować, co dalej z procesorem, więc nie musisz przeprowadzać jawnych warunkowych kontroli wszędzie w aplikacji, co może blokować, a co jest gotowe do przetworzenia.
źródło
Jeśli wątek oczekuje na zasób (na przykład ładowanie wartości z pamięci RAM do rejestru, dyskowe operacje we / wy, dostęp do sieci, uruchamianie nowego procesu, wysyłanie zapytań do bazy danych lub oczekiwanie na dane wejściowe użytkownika), procesor może pracować na inny wątek i powróć do pierwszego wątku, gdy zasób będzie dostępny. Zmniejsza to czas, jaki procesor spędza w stanie bezczynności, ponieważ procesor może wykonywać miliony operacji zamiast pozostawać w stanie bezczynności.
Rozważ wątek, który musi odczytywać dane z dysku twardego. W 2014 roku typowy rdzeń procesora działa z częstotliwością 2,5 GHz i może być w stanie wykonać 4 instrukcje na cykl. Przy czasie cyklu 0,4 ns procesor może wykonać 10 instrukcji na nanosekundę. Przy typowym mechanicznym czasie wyszukiwania dysku twardego wynoszącym około 10 milisekund, procesor jest w stanie wykonać 100 milionów instrukcji w czasie potrzebnym na odczytanie wartości z dysku twardego. Dyski twarde z małą pamięcią podręczną (bufor 4 MB) i dyski hybrydowe z kilkoma GB miejsca na dane mogą znacznie poprawić wydajność, ponieważ opóźnienie danych dla odczytów sekwencyjnych lub odczytów z sekcji hybrydowej może być o kilka rzędów wielkości szybsze.
Rdzeń procesora może przełączać się między wątkami (koszt wstrzymania i wznowienia wątku wynosi około 100 cykli zegara), podczas gdy pierwszy wątek czeka na wejście o dużym opóźnieniu (wszystko droższe niż rejestry (1 zegar) i pamięć RAM (5 nanosekund)). dyskowe I / O, dostęp do sieci (opóźnienie 250 ms), odczyt danych z płyty CD lub powolnej magistrali lub połączenie z bazą danych. Posiadanie większej liczby wątków niż rdzeni oznacza, że można wykonywać pożyteczną pracę podczas rozwiązywania zadań o dużym opóźnieniu.
Procesor ma harmonogram wątków, który przypisuje priorytet każdemu wątkowi i pozwala wątkowi na uśpienie, a następnie wznowienie działania po z góry określonym czasie. Zadaniem programu planującego wątki jest ograniczenie wyrzucania danych, które wystąpiłoby, gdyby każdy wątek wykonał zaledwie 100 instrukcji przed ponownym uśpieniem. Narzut związany z przełączaniem wątków zmniejszyłby całkowitą użyteczną przepustowość rdzenia procesora.
Z tego powodu warto podzielić problem na rozsądną liczbę wątków. Jeśli pisałeś kod w celu wykonania mnożenia macierzy, utworzenie jednego wątku na komórkę w macierzy wyjściowej może być nadmierne, podczas gdy jeden wątek na wiersz lub na n wierszy w macierzy wyjściowej może obniżyć koszty ogólne tworzenia, wstrzymywania i wznawiania wątków.
Dlatego też ważne jest przewidywanie gałęzi. Jeśli masz instrukcję if, która wymaga załadowania wartości z pamięci RAM, ale treść instrukcji if i else używa wartości już załadowanych do rejestrów, procesor może wykonać jedną lub obie gałęzie, zanim warunek zostanie oceniony. Gdy warunek powróci, procesor zastosuje wynik z odpowiedniej gałęzi i odrzuci drugą. Wykonywanie tutaj potencjalnie bezużytecznej pracy jest prawdopodobnie lepsze niż przełączanie się na inny wątek, co może prowadzić do szarpania.
Ponieważ odeszliśmy od jednordzeniowych procesorów o dużej szybkości zegara do procesorów wielordzeniowych, projekt chipów skupił się na upychaniu większej liczby rdzeni na matrycę, poprawie współdzielenia zasobów między rdzeniami, lepszymi algorytmami przewidywania gałęzi, lepszym narzutem przełączania wątków, i lepsze planowanie wątków.
źródło
Większość powyższych odpowiedzi dotyczy wydajności i jednoczesnego działania. Podejdę do tego z innego punktu widzenia.
Weźmy przykład, powiedzmy, uproszczonego programu do emulacji terminala. Musisz wykonać następujące czynności:
(Prawdziwe emulatory terminali robią więcej, w tym potencjalnie wyświetlają echo rzeczy, które wpisujesz na wyświetlaczu, ale na razie to pominiemy).
Teraz pętla do odczytu z pilota jest prosta, zgodnie z następującym pseudokodem:
Pętla do monitorowania klawiatury i wysyłania jest również prosta:
Problem polega jednak na tym, że musisz to robić jednocześnie. Kod musi teraz wyglądać bardziej tak, jeśli nie masz wątków:
Logika, nawet w tym celowo uproszczonym przykładzie, który nie bierze pod uwagę złożoności komunikacji w świecie rzeczywistym, jest dość zaciemniona. Jednak w przypadku wątków, nawet na jednym rdzeniu, dwie pętle pseudokodów mogą istnieć niezależnie bez przeplatania ich logiki. Ponieważ oba wątki będą w większości związane z operacjami we / wy, nie obciążają one procesora, nawet jeśli, ściśle mówiąc, marnują zasoby procesora bardziej niż zintegrowana pętla.
Oczywiście użycie w świecie rzeczywistym jest bardziej skomplikowane niż powyższe. Jednak złożoność zintegrowanej pętli rośnie wykładniczo w miarę dodawania kolejnych problemów do aplikacji. Logika staje się coraz bardziej fragmentaryczna i musisz zacząć używać technik takich jak automaty stanowe, procedury itp., Aby uzyskać możliwość zarządzania. Zarządzalne, ale nieczytelne. Wątkowanie sprawia, że kod jest bardziej czytelny.
Dlaczego więc nie miałbyś używać wątków?
Cóż, jeśli twoje zadania są związane z procesorem zamiast we / wy, wątkowanie w rzeczywistości spowalnia system. Wydajność ucierpi. W wielu przypadkach. („Thrashing” to częsty problem, jeśli porzucisz zbyt wiele wątków związanych z procesorem. W efekcie spędzasz więcej czasu na zmienianiu aktywnych wątków niż na uruchamianiu zawartości samych wątków). tak proste jest to, że celowo wybrałem uproszczony (i nierealistyczny) przykład. Jeśli chcesz powtórzyć to, co zostało wpisane na ekranie, masz nowy świat bólu, gdy wprowadzasz blokowanie współdzielonych zasobów. Mając tylko jeden wspólny zasób, nie stanowi to większego problemu, ale zaczyna stawać się coraz większym problemem, ponieważ masz więcej zasobów do udostępnienia.
Ostatecznie tworzenie wątków dotyczy wielu rzeczy. Na przykład chodzi o to, aby procesy związane z we / wy były bardziej responsywne (nawet jeśli ogólnie były mniej wydajne), jak niektórzy już powiedzieli. Chodzi także o ułatwienie logiki (ale tylko wtedy, gdy zminimalizujesz stan współdzielenia). Chodzi o wiele rzeczy i musisz zdecydować, czy jego zalety przeważają nad wadami w każdym przypadku z osobna.
źródło
Chociaż z pewnością możesz użyć wątków do przyspieszenia obliczeń w zależności od sprzętu, jednym z ich głównych zastosowań jest robienie więcej niż jednej rzeczy naraz ze względu na łatwość obsługi.
Na przykład, jeśli musisz wykonać pewne przetwarzanie w tle i nadal reagować na dane wejściowe interfejsu użytkownika, możesz użyć wątków. Bez wątków interfejs użytkownika zawieszałby się za każdym razem, gdy próbowano wykonać ciężkie przetwarzanie.
Zobacz także to powiązane pytanie: Praktyczne zastosowania wątków
źródło
Zdecydowanie nie zgadzam się z twierdzeniem @ kyoryu, że idealna liczba to jeden wątek na procesor.
Pomyśl o tym w ten sposób: dlaczego mamy wieloprocesorowe systemy operacyjne? Przez większość historii komputerów prawie wszystkie komputery miały jeden procesor. Jednak od lat sześćdziesiątych wszystkie „prawdziwe” komputery miały wieloprocesorowe (czyli wielozadaniowe) systemy operacyjne.
Uruchamiasz wiele programów, dzięki czemu jeden może działać, podczas gdy inne są blokowane na takie rzeczy, jak IO.
odłóżmy na bok argumenty dotyczące tego, czy wersje systemu Windows przed NT były wielozadaniowe. Od tego czasu każdy prawdziwy system operacyjny miał wielozadaniowość. Niektórzy nie ujawniają go użytkownikom, ale i tak jest tam, robiąc takie rzeczy, jak słuchanie radia w telefonie komórkowym, rozmawianie z chipem GPS, akceptowanie wejścia myszy itp.
Wątki to tylko zadania, które są nieco bardziej wydajne. Nie ma zasadniczej różnicy między zadaniem, procesem i wątkiem.
Procesor to straszna rzecz do marnowania, więc miej wiele rzeczy gotowych do użycia, kiedy tylko możesz.
Zgadzam się, że w przypadku większości języków proceduralnych, C, C ++, Java itp., Napisanie odpowiedniego kodu bezpiecznego dla wątków to dużo pracy. Z 6-rdzeniowymi procesorami dostępnymi obecnie na rynku i 16-rdzeniowymi procesorami w pobliżu, spodziewam się, że ludzie odejdą od tych starych języków, ponieważ wielowątkowość jest coraz bardziej krytycznym wymaganiem.
Niezgoda z @kyoryu to tylko IMHO, reszta to fakt.
źródło
Wyobraź sobie serwer WWW, który musi obsługiwać dowolną liczbę żądań. Musisz obsługiwać żądania równolegle, ponieważ w przeciwnym razie każde nowe żądanie musi czekać, aż wszystkie inne żądania zostaną zakończone (w tym wysłanie odpowiedzi przez Internet). W takim przypadku większość serwerów WWW ma znacznie mniej rdzeni niż liczba żądań, które zwykle obsługują.
Ułatwia to również deweloperowi serwera: wystarczy napisać program wątku, który obsługuje żądanie, nie trzeba myśleć o przechowywaniu wielu żądań, kolejności ich obsługi i tak dalej.
źródło
Wiele wątków będzie uśpionych, czekając na dane wejściowe użytkownika, wejścia / wyjścia i inne zdarzenia.
źródło
Wątki mogą pomóc w responsywności w aplikacjach interfejsu użytkownika. Ponadto możesz użyć wątków, aby uzyskać więcej pracy z rdzeni. Na przykład na pojedynczym rdzeniu jeden wątek może wykonywać operacje we / wy, a inny wykonuje obliczenia. Gdyby był jednowątkowy, rdzeń mógłby zasadniczo pozostawać bezczynny, czekając na zakończenie operacji we / wy. To dość wysoki przykład, ale wątki zdecydowanie można wykorzystać do nieco mocniejszego uderzenia w procesor.
źródło
Procesor lub CPU to fizyczny układ, który jest podłączony do systemu. Procesor może mieć wiele rdzeni (rdzeń jest częścią układu, która jest zdolna do wykonywania instrukcji). Rdzeń może wydawać się systemowi operacyjnemu wiele procesorów wirtualnych, jeśli jest zdolny do jednoczesnego wykonywania wielu wątków (wątek to pojedyncza sekwencja instrukcji).
Proces to inna nazwa aplikacji. Ogólnie procesy są od siebie niezależne. Jeśli jeden proces umiera, nie powoduje to śmierci innego procesu. Procesy mogą się komunikować lub współużytkować zasoby, takie jak pamięć lub we / wy.
Każdy proces ma oddzielną przestrzeń adresową i stos. Proces może zawierać wiele wątków, z których każdy może jednocześnie wykonywać instrukcje. Wszystkie wątki w procesie mają tę samą przestrzeń adresową, ale każdy wątek będzie miał swój własny stos.
Mamy nadzieję, że te definicje i dalsze badania wykorzystujące te podstawy pomogą ci zrozumieć.
źródło
Idealne użycie wątków to w rzeczywistości jeden na rdzeń.
Jednak jeśli nie używasz wyłącznie asynchronicznych / nieblokujących operacji we / wy, istnieje duża szansa, że w pewnym momencie zostaną zablokowane wątki na we / wy, które nie będą używać procesora.
Ponadto typowe języki programowania utrudniają użycie 1 wątku na procesor. Języki zaprojektowane pod kątem współbieżności (takie jak Erlang) mogą ułatwić nieużywanie dodatkowych wątków.
źródło
Sposób projektowania niektórych interfejsów API sprawia, że nie masz wyboru tylko uruchomić je w osobnym wątku (wszystko z operacjami blokującymi). Przykładem mogą być biblioteki HTTP Pythona (AFAIK).
Zwykle nie stanowi to jednak większego problemu (jeśli jest to problem, system operacyjny lub API powinny być dostarczane z alternatywnym asynchronicznym trybem pracy, tj .:)
select(2)
, ponieważ prawdopodobnie oznacza to, że wątek będzie spał podczas oczekiwania na I / O ukończenie. Z drugiej strony, jeśli coś robi obliczenia ciężki, ty masz go umieścić w osobnym wątku niż powiedzmy, wątek GUI (chyba że lubisz ręcznego Multiplexing).źródło
Wiem, że to bardzo stare pytanie z wieloma dobrymi odpowiedziami, ale jestem tutaj, aby wskazać coś, co jest ważne w obecnym środowisku:
Jeśli chcesz zaprojektować aplikację do obsługi wielu wątków, nie powinieneś projektować pod kątem określonego ustawienia sprzętowego. Technologia procesorów rozwija się dość szybko od lat, a liczba rdzeni stale rośnie. Jeśli celowo projektujesz swoją aplikację tak, aby wykorzystywała tylko 4 wątki, wtedy potencjalnie ograniczasz się do systemu ośmiordzeniowego (na przykład). Obecnie na rynku dostępne są nawet systemy 20-rdzeniowe, więc taka konstrukcja zdecydowanie przynosi więcej szkody niż pożytku.
źródło
W odpowiedzi na Twoje pierwsze przypuszczenie: maszyny wielordzeniowe mogą jednocześnie uruchamiać wiele procesów, a nie tylko wiele wątków pojedynczego procesu.
Odpowiadając na pierwsze pytanie: celem wielu wątków jest zwykle jednoczesne wykonywanie wielu zadań w ramach jednej aplikacji. Klasyczne przykłady w sieci to program pocztowy wysyłający i odbierający pocztę oraz serwer WWW odbierający i wysyłający żądania stron. (Zauważ, że zasadniczo niemożliwe jest zredukowanie systemu takiego jak Windows do uruchamiania tylko jednego wątku lub nawet tylko jednego procesu. Uruchom Menedżera zadań systemu Windows, a zazwyczaj zobaczysz długą listę aktywnych procesów, z których wiele będzie uruchamiać wiele wątków. )
W odpowiedzi na drugie pytanie: większość procesów / wątków nie jest związana z procesorem (tj. Nie działa w sposób ciągły i nieprzerwany), ale zamiast tego często zatrzymuje się i czeka na zakończenie operacji we / wy. Podczas tego oczekiwania inne procesy / wątki mogą działać bez „kradzieży” czekającego kodu (nawet na jednym rdzeniu maszyny).
źródło
Wątek jest abstrakcją, która umożliwia Ci pisanie kodu tak prostego jak sekwencja operacji, błogo nieświadomego, że kod jest wykonywany z przeplotem z innym kodem, zaparkowany w oczekiwaniu na IO lub (może nieco bardziej świadomy) czekając na inne wątki wydarzenia lub wiadomości.
źródło
Chodzi o to, że ogromna większość programistów nie rozumie, jak zaprojektować maszynę stanową. Możliwość umieszczenia wszystkiego we własnym wątku uwalnia programistę od konieczności zastanawiania się, jak efektywnie przedstawić stan różnych obliczeń w toku, aby można je było przerwać, a następnie wznowić.
Jako przykład rozważ kompresję wideo, zadanie bardzo intensywne dla procesora. Jeśli używasz narzędzia GUI, prawdopodobnie chcesz, aby interfejs pozostawał responsywny (pokazywał postęp, odpowiadał na żądania anulowania, zmianę rozmiaru okna itp.). Dlatego projektujesz oprogramowanie kodera tak, aby przetwarzało dużą jednostkę (jedną lub więcej ramek) naraz i uruchamiało je we własnym wątku, niezależnie od interfejsu użytkownika.
Oczywiście, gdy zdasz sobie sprawę, że byłoby miło móc zapisać stan kodowania w toku, aby móc zamknąć program, aby ponownie uruchomić lub zagrać w grę wymagającą zasobów, zdajesz sobie sprawę, że powinieneś nauczyć się projektować maszyny stanu z początek. Albo to, albo zdecydujesz się zaprojektować zupełnie nowy problem hibernacji procesów w systemie operacyjnym, abyś mógł zawiesić i wznowić poszczególne aplikacje na dysk ...
źródło