Co stanowi właściwe wykorzystanie wątków w programowaniu?

13

Mam dość słuchania, jak ludzie zalecają używanie tylko jednego wątku na procesor, podczas gdy wiele programów używa do 100 na proces! Weźmy na przykład niektóre popularne programy

vb.net ide uses about 25 thread when not debugging
System uses about 100
chrome uses about 19
Avira uses more than about 50

Za każdym razem, gdy zamieszczam pytanie związane z wątkiem, prawie za każdym razem przypomina mi się, że nie powinienem używać więcej niż jednego wątku na procesor, a wszystkie programy, o których wspomniałem powyżej, psują się w moim systemie z jednym procesorem.

Kowal
źródło
7
To zalecenie jest ogólne. Limit jednego wątku na procesor jest odpowiedni tylko dla aplikacji związanych z obliczeniami. Większość programów jest związana z IO, niezależnie od tego, czy chodzi o ruch sieciowy, dostęp do dysku, a nawet pamięć RAM. Właśnie dlatego serwery WWW, bazy danych itp. Mają pule wątków o wiele większej liczbie wątków niż rdzenie procesorów.
Kilian Foth
2
„Niemal za każdym razem przypomina mi się, że nie powinienem używać więcej niż jednego wątku na procesor”? Czy możesz publikować linki lub przykłady? Prawie za każdym razem?
S.Lott,
2
„... ludzie zalecają używanie tylko jednego wątku na proces”. Kim są Ci ludzie? Planowanie znacznie się rozwinęło od czasów średniowiecza.
Rein Henrichs
2
Nie powinieneś mieć więcej niż jednego wątku interfejsu użytkownika na proces.
SLaks
3
@Billy ONeal, twoja edycja sprawiła, że ​​pytanie nie miało znaczenia
SK-logic

Odpowiedzi:

22

powinieneś użyć tylko jednego wątku na procesor,

Być może w HPC, gdzie chcesz maksymalnej wydajności - ale poza tym najgłupsza rzecz, jaką dziś słyszałem!

Powinieneś użyć liczby wątków odpowiednich dla projektu programu i nadal dawać zadowalającą wydajność.

W przypadku serwera WWW uzasadnione może być uruchomienie wątku dla każdego połączenia przychodzącego (chociaż istnieją lepsze sposoby dla bardzo mocno obciążonych serwerów).

Dla ide, każde narzędzie działające w swoim własnym wątku nie jest nierozsądne. Podejrzewam, że wiele wątków zgłoszonych dla .Net IDE to np. Logowanie i zadania we / wy uruchamiane w ich własnych wątkach, aby mogły być dalej odblokowane.

Martin Beckett
źródło
9
Teraz zastanawiam się, jaka jest najgłupsza rzecz, jaką kiedykolwiek słyszałeś!
Michael K
3
@Michael - Uczyłem studentów i pracowałem na kontraktach obronnych - nie uwierzyłbyś w najgłupsze rzeczy, jakie słyszałem!
Martin Beckett,
1
Czy widzieliśmy je na TheDailyWTF.com?
FrustratedWithFormsDesigner
tak naprawdę nie mogę ich teraz znaleźć, ale spójrz na ten link social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/…
Smith
2
Mają maksymalnie jeden wątek związany z procesorem na procesor przypisany do aplikacji. Wątki związane z operacjami wejścia-wyjścia nie stanowią dużego problemu (oprócz pamięci, którą zużywają) i należy pamiętać, że aplikacje można ograniczyć do korzystania tylko z podzbioru procesorów systemu; w końcu to (zwykle) komputer użytkownika / administratora, a nie programista.
Donal Fellows,
2

Porada dotycząca jednego wątku na rdzeń ma zastosowanie, gdy celem jest szybkość poprzez równoległe wykonywanie.

Zupełnie innym i równie ważnym powodem jest prostota kodu, który musi reagować na nieprzewidziane zdarzenia. Więc jeśli program musi nasłuchiwać na 100 gniazdach i wydaje się, że poświęca każdemu z nich pełną uwagę, jest to idealne zastosowanie do wątkowania. Innym przykładem jest interfejs użytkownika, w którym jeden wątek obsługuje zdarzenia interfejsu użytkownika, podczas gdy inny wykonuje przetwarzanie w tle.

Mike Dunlavey
źródło
1
Przetwarzanie związane z operacjami we / wy można wykonać jako jeden wątek na źródło zdarzenia lub wiele źródeł zdarzeń można multipleksować na jednym wątku. Kod multipleksowany jest zwykle zarówno bardziej złożony, jak i bardziej wydajny.
Donal Fellows
2

Potrzebujesz jednego wątku dla każdego obliczenia, które może przebiegać z inną szybkością niż inne obliczenia.

W przypadku równoległych obliczeń związanych z procesorem, które występują w dużych blokach pracy, zwykle potrzebujesz jednego wątku na procesor, ponieważ gdy wszystkie są zajęte, więcej wątków nie pomaga i po prostu tworzy narzut harmonogramu. Jeśli bloki pracy mają nieregularne rozmiary w czasie lub są generowane dynamicznie w czasie wykonywania (często zdarza się, gdy masz do przetworzenia duże złożone struktury danych), możesz chcieć dołączyć te bloki do wielu wątków, więc harmonogram zawsze ma duży ustawić, aby wybrać, kiedy zakończy się jakiś blok pracy, aby wszystkie procesory były zajęte.

W przypadku obliczeń związanych z we / wy zazwyczaj potrzebujesz jednego wątku dla każdego niezależnego „kanału” we / wy, ponieważ komunikują się one z różnymi prędkościami, a wątki blokowane na kanale nie uniemożliwiają postępów innym wątkom.

Ira Baxter
źródło
Pamiętaj tylko, że ten styl wątkowania może prowadzić do dziwnie skonstruowanych programów. Widziałem 4-wątkowy program, który miał wątek do odczytywania rekordów z tabeli DB, wątek do zapisywania przekształconych rekordów w gnieździe, wątek do odczytywania odpowiedzi na te zapisy do gniazd (które wróciły poza kolejnością i asynchronicznie) oraz wątek do zmodyfikowania oryginalnego rekordu DB z odpowiedzią. Wystąpiły nieintuicyjne warunki błędu.
Bruce Ediger
Jednym z poglądów jest to, że ten styl tworzy nieparzyste programy. Innym poglądem jest naturalny styl, który powinny mieć programy. Nie wiem o „nieintuicyjnych” warunkach błędu; jeśli dzieje się wiele rzeczy, a jedna z nich zawiera błąd, upewnienie się, że jest prawidłowo propagowana w obliczeniach asynchronicznych, jest problemem dla wielu języków [głupio, wyjątki Java nie są zdefiniowane na granicach wątków], ale nie są problem ze stylem programu. (Nasz język programowania PARLANSE [patrz moje bio] doskonale obsługuje wyjątki przekraczające granice wątków, więc można to zrobić poprawnie).
Ira Baxter
1

Ogólna zasada dla wątków polega na tym, że chcesz mieć co najmniej jeden „aktywny” (zdolny do wykonania poleceń natychmiast podany czas procesora) wątek roboczy dla każdej „jednostki wykonawczej” dostępnej na komputerze. „Jednostka wykonawcza” to jeden logiczny procesor instrukcji, więc czterordzeniowy, czterordzeniowy serwer Xeon z hiperwątkiem miałby 32 EU (4 chipy, 4 rdzenie na chip, każdy z hiperwątkiem). Twój średni Core i7 miałby 8.

Jeden wątek na UE to najpełniejsze wykorzystanie mocy procesora, pod warunkiem że wątki będą zawsze w stanie roboczym; prawie nigdy tak nie jest, ponieważ wątki potrzebują dostępu do pamięci niebuforowanej, dysku twardego, portów sieciowych itp., na które muszą czekać i które nie wymagają aktywnej uwagi procesora. W ten sposób możesz jeszcze bardziej zwiększyć ogólną wydajność dzięki większej ilości wątków ustawionych w kolejce i gotowych do użycia. To kosztuje. gdy procesor przełącza wątek, musi buforować rejestry wątku, wskaźnik wykonania i inne informacje o stanie, zwykle przechowywane w najgłębszych działaniach UE i bardzo szybko dostępne, umożliwiając innym UE w tym układzie procesora na pobranie go. Wymaga również wątków w systemie operacyjnym, aby zdecydować, na który wątek należy przełączyć. Wreszcie, kiedy UE zmienia wątki, traci przyrost wydajności potoku, z którego korzysta większość architektur procesorów; musi przepłukać rurociąg przed przełączeniem wątków. Ponieważ jednak wszystko to zajmuje średnio o wiele mniej czasu niż zwykłe czekanie, aż dysk twardy, a nawet pamięć RAM wróci z informacjami, jest to warte kosztów.

Jednak ogólnie rzecz biorąc, gdy przekroczysz dwukrotnie liczbę „aktywnych” wątków w porównaniu z UE, system operacyjny zaczyna spędzać więcej czasu na wątkach planowania w UE, a UE spędza więcej czasu na przełączaniu się między nimi, niż faktycznie spędza na uruchamianiu aktywnych wątków programów. To jest punkt braku ekonomii skali; uruchomienie wielowątkowego algorytmu potrwa dłużej, jeśli w tym momencie dodasz dodatkowy wątek.

Ogólnie rzecz biorąc, chcesz zachować co najmniej tyle wątków w swoim programie, ile masz UE na komputerze, ale chcesz uniknąć ponad dwukrotnej liczby tych, które nie czekają ani nie śpią.

KeithS
źródło
Jeśli N jest liczbą wątków, a U liczbą jednostek, OP kwestionuje zasadę „N = U”. Rozluźniasz to zgodnie z regułą „U <= N <= 2 U”. Chciałbym pójść nieco dalej i powiedzieć, że „N <= c U” dla „rozsądnie małej” stałej (znanej programistowi) c jest dopuszczalne (jeśli testy porównawcze wykazują rozsądną wydajność). Byłbym bardzo zaniepokojony, jeśli liczba wątków może wzrosnąć do potencjalnie nieograniczonej liczby.
5gon12eder
1

Powinieneś użyć jednego wątku do:

Każdy procesor musi być zajęty.

Każde wejście / wyjście, które możesz użytecznie jednocześnie wstrzymać, którego nie możesz wykonać w sposób nieblokujący. (Na przykład czyta z dysku lokalnego).

Każde zadanie wymagające dedykowanego wątku, na przykład wywołanie do biblioteki, która nie ma nieblokującego interfejsu lub gdzie interfejsy nieblokujące są nieodpowiednie. Obejmuje to zadania takie jak monitorowanie zegara systemowego, liczniki czasu odpalania i tak dalej.

Kilka dodatkowych w celu ochrony przed nieoczekiwanym blokowaniem, takim jak błędy strony.

Kilka dodatkowych w celu ochrony przed oczekiwanym blokowaniem, których nie warto optymalizować, na przykład w niekrytycznym kodzie. (Na przykład, jeśli bardzo rzadko musisz wykonać żądanie DNS, prawdopodobnie nie warto podejmować wysiłku, aby wykonywać żądania DNS asynchronicznie. Po prostu utwórz kilka dodatkowych wątków i ułatw swoje życie).

Jeśli zastosujesz zasadę „jeden wątek na procesor”, cały kod ma krytyczne znaczenie dla wydajności. Każdy kod, który z jakiegoś powodu blokuje, oznacza, że ​​proces nie może korzystać z tego procesora. To sprawia, że ​​programowanie jest trudniejsze bez powodu.

David Schwartz
źródło
0

Możesz albo spawnować procesy i wątki, aby umożliwić wykorzystanie systemu wielordzeniowego \ wieloprocesorowego dla jednego programu, w którym to przypadku nie zyskujesz (przynajmniej dla jednego programu) posiadania większej liczby wątków \ ​​procesów niż rdzeni.

Lub możesz mieć procedury odpytujące o zdarzenie, które zwykle blokuje dalsze wykonywanie. Zamiast powiązać procesor z odpytywaniem, możesz zamiast tego utworzyć wątek, który będzie siedział w stanie bezczynności, dopóki odpowiednie zdarzenie go nie obudzi. Ta metoda jest bardzo często stosowana w serwerach WWW i kolejkach zdarzeń GUI. Większość programów chce mieć jakiś centralny magazyn danych (nawet jeśli jest to jego kod wykonania programu), do którego mają dostęp wszystkie wątki, więc sądzę, że właśnie dlatego używają łączenia wątków nad procesami.

Peter Smith
źródło
0

Wspomniane aplikacje rzadko uruchamiają te dziesiątki wątków jednocześnie. Większość z nich po prostu tam siedzi, ponieważ są w puli wątków . Aplikacja wysyła różne zadania do kolejki, która jest czyszczona przez wątki w puli wątków.

Dlaczego więc basen jest tak duży? Ponieważ często wątki muszą czekać na inne zasoby, takie jak dysk, sieć, użytkownik, inny wątek itp. Podczas wątku należy uruchomić inne wątki, aby w pełni wykorzystać procesor. Właściwe dobranie puli jest jednak trudne. Za mało wątków, a stracisz wydajność, ponieważ procesor nie jest w pełni wykorzystywany podczas oczekiwania na coś. Zbyt wiele wątków, a stracisz wydajność z powodu przełączania się między nimi.

Joonas Pulakka
źródło