Jak wyjaśnić, dlaczego wielowątkowość jest trudna

84

Jestem dość dobrym programistą, mój szef jest również dość dobrym programistą. Chociaż wydaje się nie doceniać niektórych zadań, takich jak wielowątkowość i tego, jak trudne może być (uważam, że bardzo trudno jest zrobić coś więcej niż uruchomienie kilku wątków, czekanie na zakończenie wszystkich, a następnie zwracanie wyników).

W momencie, gdy zaczynasz martwić się impasami i warunkami wyścigu, jest mi bardzo trudno, ale wydaje się, że szef nie docenia tego - nie sądzę, żeby kiedykolwiek się z tym spotkał. Wystarczy uderzyć w niego blokadą, to prawie postawa.

Jak więc mogę go przedstawić lub wyjaśnić, dlaczego nie docenia złożoności współbieżności, równoległości i wielowątkowości? A może się mylę?

Edycja: Trochę o tym, co zrobił - przeglądaj listę, dla każdego elementu na tej liście utwórz wątek, który wykonuje polecenie aktualizacji bazy danych na podstawie informacji w tym elemencie. Nie jestem pewien, w jaki sposób kontrolował liczbę wątków wykonanych jednocześnie, chyba dodałby je do kolejki, gdyby było ich zbyt wiele (nie użyłby semafora).

Pan Shoubs
źródło
17
Wielowątkowość jest łatwa. Prawidłowa synchronizacja jest trudna.
Vineet Reynolds
33
Zaprowadź trzy osoby do pokoju, najlepiej z różnymi akcentami, i niech wyjaśnią różne, pokrywające się części problemu współbieżności .... jednocześnie.
greyfade
Wielowątkowość może być bardzo trudna lub bardzo łatwa, w zależności od problemu i obsługi języka. Clojure ułatwia clojure.org/concurrent_programming
Job
4
@ Job Współbieżne programowanie jest zawsze trudne (w projektach z prawdziwego świata), bez względu na używany język. Scala, Clojure lub Erlang sprawiają, że jest to trochę rozsądne, gdy chcesz porównać go z językami, które używają i zachęcają do stanów zmiennych.
Chiron
4
Moja ulubiona metafora to: „Czy wziąłbyś jednocześnie pigułkę nasenną i środek przeczyszczający?” Nawet przy użyciu złożonych kolejek wiadomości porządek jest owocem prawidłowej realizacji . To, o ile nie masz z tym dużego doświadczenia, jest trudne dla wielu osób.
Tim Post

Odpowiedzi:

29
  1. Jeśli możesz liczyć na jakiekolwiek doświadczenie matematyczne, zilustruj, w jaki sposób normalny przepływ wykonywania, który jest zasadniczo deterministyczny, staje się nie tylko niedeterministyczny z kilkoma wątkami, ale wykładniczo złożony, ponieważ musisz upewnić się, że każde możliwe przeplatanie instrukcji maszynowych nadal będzie słuszne. Prostym przykładem zagubionej aktualizacji lub niepoprawnego odczytu jest często przyciągający wzrok.

  2. „Uderz w to” jest trywialnym rozwiązaniem ... rozwiązuje wszystkie problemy, jeśli nie martwisz się wydajnością. Spróbuj zilustrować, jaki byłby hit wydajności, gdyby na przykład Amazon musiał zamknąć całe wschodnie wybrzeże, ilekroć ktoś w Atlancie zamówi jedną książkę!

Kilian Foth
źródło
1
+1 za dyskusję o złożoności matematycznej - w ten sposób zrozumiałem trudność w współbieżności w stanie współdzielonym i jest to argument, który generalnie popieram, opowiadając się za architekturami przekazującymi wiadomości. -1 dla „uderz blokadą” ... Wyrażenie to odnosi się do bezmyślnego podejścia do korzystania z blokad, co najprawdopodobniej doprowadzi do impasu lub niespójnego zachowania (ponieważ klienci twojego kodu, którzy żyją w różnych wątkach, powodują konflikt żądań, ale nie synchronizują się między sobą, klienci będą mieli niekompatybilne modele stanu biblioteki).
Aidan Cully
2
Amazon będzie musiał zablokować inwentaryzacji pojedynczych przedmiotów w jednym magazynie krótko podczas przetwarzania zamówienia. W przypadku nagłego, ogromnego uruchomienia jednego konkretnego przedmiotu, wydajność zamówienia dla tego przedmiotu będzie cierpieć, dopóki zapasy nie zostaną wyczerpane, a dostęp do ekwipunku stanie się tylko do odczytu (a zatem w 100% możliwy do udostępnienia). Amazon postąpił tak, że inne programy tego nie robią, a mianowicie możliwość kolejkowania zamówień do momentu ponownej zapasy i możliwość obsługi zamówień w kolejce przed ponownym zapasem w przypadku nowych zamówień.
Blrfl
@Blrfl: Programy mogą to zrobić, jeśli są napisane, aby używać przekazywania wiadomości przez kolejki. Nie ma potrzeby, aby wszystkie wiadomości do określonego wątku przechodziły przez jedną kolejkę…
Donal Fellows
4
@Donal Fellows: Jeśli w magazynie znajduje się 1 mln widżetów, a zamówienia w wysokości 1 mln pojawiają się w tym samym momencie, wszystkie zamówienia są serializowane na pewnym poziomie, jednocześnie dopasowując przedmioty do zamówień bez względu na sposób ich obsługi. Praktyczna rzeczywistość jest taka, że ​​Amazon prawdopodobnie nigdy nie ma tak wielu widżetów w magazynie, że opóźnienia w przetwarzaniu zgniatania zamówień stają się niedopuszczalnie wysokie, zanim wyczerpią się zapasy, a wszystkim innym w linii można powiedzieć (równolegle): „nie ma nas. „ Kolejki wiadomości to świetny sposób na zapobieganie zakleszczeniom, ale nie rozwiązują problemu wysokiej rywalizacji o ograniczone zasoby.
Blrfl
79

Wielowątkowość jest prosta. Kodowanie aplikacji do wielowątkowości jest bardzo, bardzo łatwe.

Istnieje prosta sztuczka polegająca na użyciu dobrze zaprojektowanej kolejki komunikatów ( nie rozwijaj własnej) do przesyłania danych między wątkami.

Najtrudniejszą częścią jest próba magicznej aktualizacji wielu wątków przez wspólny obiekt. Wtedy staje się podatny na błędy, ponieważ ludzie nie zwracają uwagi na obecne warunki wyścigowe.

Wielu ludzi nie używa kolejek wiadomości i próbuje aktualizować współdzielone obiekty i stwarzać problemy dla siebie.

Trudne staje się zaprojektowanie algorytmu, który działa dobrze podczas przesyłania danych między kilkoma kolejkami. To jest trudne. Ale mechanika współistniejących wątków (poprzez wspólne kolejki) jest łatwa.

Należy również pamiętać, że wątki współużytkują zasoby we / wy. Program związany z operacjami we / wy (tj. Połączenia sieciowe, operacje na plikach lub operacje na bazach danych) prawdopodobnie nie będzie działał szybciej z dużą liczbą wątków.

Jeśli chcesz zilustrować problem aktualizacji obiektu współdzielonego, to proste. Usiądź naprzeciwko stołu z bukietem kart papierowych. Zapisz prosty zestaw obliczeń - 4 lub 6 prostych wzorów - z dużą ilością miejsca na stronie.

Oto gra. Każdy z was czyta formułę, pisze odpowiedź i odkłada kartę z odpowiedzią.

Każdy z was wykona połowę pracy, prawda? Skończyłeś w połowie czasu, prawda?

Jeśli twój szef nie myśli dużo i po prostu zaczyna, skończysz w jakiś sposób na konflikcie i obaj piszecie odpowiedzi na tę samą formułę. To nie zadziałało, ponieważ między wami czytacie przed napisaniem. Nic nie stoi na przeszkodzie, abyś czytał tę samą formułę i nadpisywał odpowiedzi.

Istnieje wiele sposobów tworzenia warunków wyścigu przy użyciu źle lub niezablokowanych zasobów.

Jeśli chcesz uniknąć wszystkich konfliktów, możesz pociąć papier na stos formuł. Zdejmujesz jedną z kolejki, zapisujesz odpowiedź i publikujesz odpowiedzi. Brak konfliktów, ponieważ oboje czytacie z kolejki komunikatów tylko do odczytu.

S.Lott
źródło
Nawet pocięcie papieru na stos nie naprawia całkowicie rzeczy - wciąż masz sytuację, w której ty i twój szef jednocześnie sięgacie po nową formułę i uderzacie go w kostki. W rzeczywistości powiedziałbym, że jest to reprezentatywne dla najczęstszego rodzaju problemu z wątkami. Naprawdę poważne błędy znajdują się wcześnie. Naprawdę niezwykłe błędy pozostają na zawsze, ponieważ nikt nie jest w stanie ich odtworzyć. Prawdopodobne warunki wyścigu - takie jak ten - pojawiają się w testach, a ostatecznie wszystkie (lub bardziej prawdopodobne) większość z nich zostaje wyeliminowana.
Airsource Ltd,
@AirsourceLtd Co dokładnie mówisz, „uderzając pięściami w jego”? Tak długo, jak masz kolejkę komunikatów, która uniemożliwia odbieranie tej samej wiadomości przez dwa różne wątki, nie będzie problemu. Chyba że nie rozumiem, co miałeś na myśli.
Zack,
25

Programowanie wielowątkowe jest prawdopodobnie najtrudniejszym rozwiązaniem dla współbieżności. Zasadniczo jest to dość niska abstrakcja tego, co faktycznie robi maszyna.

Istnieje wiele podejść, takich jak model aktora lub pamięć transakcyjna (programowa) , które są znacznie łatwiejsze. Lub praca z niezmiennymi strukturami danych (takimi jak listy i drzewa).

Zasadniczo odpowiednie rozdzielenie problemów ułatwia wielowątkowość. Coś, o czym często się zapomina, gdy ludzie spawnują 20 wątków, wszystkie próbują przetworzyć ten sam bufor. Używaj reaktorów tam, gdzie potrzebujesz synchronizacji i ogólnie przesyłaj dane między różnymi pracownikami za pomocą kolejek komunikatów.
Jeśli masz blokadę w logice aplikacji, zrobiłeś coś złego.

Tak, technicznie rzecz biorąc, wielowątkowość jest trudna.
„Slap a lock on it” to właściwie najmniej skalowalne rozwiązanie problemów z współbieżnością, i w rzeczywistości pokonuje cały cel wielowątkowości. Powoduje to przywrócenie problemu z powrotem do modelu współbieżnego wykonywania. Im częściej to robisz, tym bardziej prawdopodobne jest, że w danym momencie działa tylko jeden wątek (lub 0 w impasie). Pokonuje cały cel.
To tak, jakby powiedzieć: „Rozwiązanie problemów trzeciego świata jest łatwe. Wystarczy rzucić na niego bombę”. Tylko dlatego, że istnieje trywialne rozwiązanie, nie czyni to problemu trywialnym, ponieważ zależy Ci na jakości wyniku.

Ale w praktyce rozwiązywanie tych problemów jest tak samo trudne jak wszelkie inne problemy programistyczne i najlepiej wykonywać je przy użyciu odpowiednich abstrakcji. Co w rzeczywistości jest dość łatwe.

back2dos
źródło
14

Myślę, że pytanie to nie ma technicznego charakteru - IMO to kwestia zaufania. Często jesteśmy proszeni o odtwarzanie skomplikowanych aplikacji, takich jak - och, nie wiem - na przykład Facebook. Doszedłem do wniosku, że jeśli musisz wyjaśnić złożoność zadania niewtajemniczonemu / kierownictwu - w Danii coś jest zepsute.

Nawet jeśli inni programiści ninja mogliby wykonać to zadanie w 5 minut, twoje szacunki oparte są na twoich osobistych zdolnościach. Twój rozmówca powinien albo nauczyć się ufać twojej opinii w tej sprawie, albo zatrudnić kogoś, kogo słowo jest w stanie zaakceptować.

Wyzwanie nie polega na przekazywaniu implikacji technicznych, które ludzie albo ignorują, albo nie są w stanie zrozumieć podczas rozmowy, ale na ustanawianiu relacji wzajemnego szacunku.

sunwukung
źródło
1
Ciekawa odpowiedź, choć jest to pytanie techniczne. Zgadzam się jednak z tym, co mówisz ... w tym przypadku jednak mój menedżer jest całkiem dobrym programistą, ale myślę, że ponieważ nie spotkał się ze złożonością aplikacji wielowątkowych, nie docenia ich.
Pan Shoubs,
6

Jednym prostym eksperymentem myślowym mającym na celu zrozumienie impasu jest problem „ filozofa ”. Jednym z przykładów, których używam do opisania, jak złe mogą być warunki wyścigowe, jest sytuacja Therac 25 .

„Po prostu uderzanie zamkiem” to mentalność kogoś, kto nie napotkał trudnych błędów w wielowątkowości. I możliwe, że myśli, że przeceniasz powagę sytuacji (ja nie - możliwe jest wysadzenie rzeczy w powietrze lub zabicie ludzi z błędami warunków wyścigowych, zwłaszcza z wbudowanym oprogramowaniem, które kończy się w samochodach).

Tangurena
źródło
1
tzn. problem z kanapkami: robisz kupę kanapek, ale jest tylko 1 talerz masła i 1 nóż. Ogólnie wszystko jest w porządku, ale w końcu ktoś złapie masło, podczas gdy ktoś inny złapie nóż ... a potem oboje staną tam i czekają, aż drugi uwolni swoje zasoby.
gbjbaanb
Czy takie problemy z impasem można rozwiązać, zawsze zdobywając zasoby w ustalonej kolejności?
compman
@compman, no. Ponieważ możliwe jest, że 2 wątki spróbują pobrać ten sam zasób w tym samym momencie, a te wątki niekoniecznie potrzebują tego samego zestawu zasobów - wystarczy nakładanie się, aby powodować problemy. Jednym ze schematów jest odłożenie zasobu z powrotem, a następnie odczekanie losowego okresu przed ponownym skorzystaniem z niego. Ten okres wycofywania ma miejsce w wielu protokołach, z których najwcześniej był Aloha. en.wikipedia.org/wiki/ALOHAnet
Tangurena
1
Co jeśli każdy zasób w programie ma numer, a gdy wątek / proces potrzebuje zestawu zasobów, zawsze blokuje zasoby w rosnącej kolejności numerycznej? Nie sądzę, aby impas mógł się zdarzyć.
compman
1
@compman: To rzeczywiście sposób na uniknięcie impasu. Możliwe jest zaprojektowanie narzędzi, które pozwolą automatycznie to sprawdzić; więc jeśli Twoja aplikacja nigdy nie blokuje zasobów innych niż w rosnącym porządku numerycznym, to nigdy nie miałeś potencjalnego impasu. (Należy pamiętać, że potencjalne zakleszczenia zawsze zamieniają się w prawdziwe zakleszczenia tylko wtedy, gdy kod działa na komputerze klienta).
gnasher729
3

Współbieżne aplikacje nie są deterministyczne. Przy wyjątkowo małej ilości całego kodu, który programista uznał za podatny na atak, nie kontrolujesz, kiedy część wątku / procesu zostanie wykonana w stosunku do dowolnej części innego wątku. Testowanie jest trudniejsze, trwa dłużej i jest mało prawdopodobne, aby znaleźć wszystkie defekty związane z współbieżnością. Wad, jeśli zostaną wykryte, są często subtelne, nie można ich konsekwentnie odtwarzać, dlatego ich naprawa jest trudna.

Dlatego jedyną poprawną aplikacją współbieżną jest taka, która jest poprawna, co nie jest często praktykowane w tworzeniu oprogramowania. W rezultacie odpowiedź S.Lota jest najlepszą ogólną radą, ponieważ przekazywanie wiadomości jest stosunkowo łatwe do udowodnienia.

mattnz
źródło
3

Krótka odpowiedź w dwóch słowach: NIEDETERMINIZM OBSERWACYJNY

Długa odpowiedź: zależy od tego, jakiego podejścia do programowania współbieżnego używasz, biorąc pod uwagę problem. W książce Koncepcje, techniki i modele programowania komputerowego autorzy jasno wyjaśniają cztery główne praktyczne podejścia do pisania współbieżnych programów:

  • Programowanie sekwencyjne : podejście bazowe, które nie ma współbieżności;
  • Deklaratywna współbieżność : stosowana, gdy nie ma zauważalnego niedeterminizmu;
  • Wiadomość pominięciem współbieżności : współbieżne przekazywania wiadomości pomiędzy wieloma podmiotami, gdzie każda jednostka wewnętrznie rozpatruje kolejno wiadomość;
  • Współbieżność stanu współdzielonego : aktualizacja wątku współdzielonych obiektów pasywnych za pomocą gruboziarnistych działań atomowych, np. Blokad, monitorów i transakcji;

Teraz najłatwiejszym z tych czterech podejść oprócz oczywistego programowania sekwencyjnego jest deklaratywna współbieżność , ponieważ programy napisane przy użyciu tego podejścia nie mają zauważalnego niedeterminizmu . Innymi słowy, nie ma warunków rasowych , ponieważ warunki rasowe są jedynie obserwowalnym zachowaniem niedeterministycznym.

Ale brak zauważalnego niedeterminizmu oznacza, że ​​istnieją pewne problemy , których nie możemy rozwiązać za pomocą deklaratywnej współbieżności. Oto miejsce, w które wchodzą dwie ostatnie niełatwe podejścia. Nie tak łatwa część jest konsekwencją zauważalnego niedeterminizmu. Teraz oba są objęte stanowym współbieżnym modelem i są również równoważne pod względem ekspresji. Ale ze względu na stale rosnącą liczbę rdzeni na procesor, wydaje się, że branża ostatnio zainteresowała się większą współbieżnością przekazywania komunikatów, co można zaobserwować we wzroście bibliotek przekazywania komunikatów (np. Akka dla JVM) lub języków programowania (np. Erlang ) .

Wspomniana wcześniej biblioteka Akka, poparta teoretycznym modelem aktora, upraszcza budowanie współbieżnych aplikacji, ponieważ nie musisz już zajmować się zamkami, monitorami ani transakcjami. Z drugiej strony wymaga innego podejścia do projektowania rozwiązania, tj. Myślenia w sposób hierarchiczny, złożony z aktorów. Można powiedzieć, że wymaga to zupełnie odmiennego sposobu myślenia, co w końcu może być jeszcze trudniejsze niż korzystanie ze współbieżności współdzielonej w stanie zwykłym.

Współbieżne programowanie jest trudne z powodu zauważalnego niedeterminizmu, ale przy stosowaniu odpowiedniego podejścia do danego problemu i odpowiedniej biblioteki, która obsługuje to podejście, można uniknąć wielu problemów.

Jernej Jerin
źródło
0

Najpierw nauczono mnie, że może wywoływać problemy, widząc prosty program, który uruchomił 2 wątki i kazał im drukować na konsoli w tym samym czasie od 1 do 100. Zamiast:

1
1
2
2
3
3
...

Otrzymujesz coś takiego:

1
2
1
3
2
3
...

Uruchom go ponownie, aby uzyskać zupełnie inne wyniki.

Większość z nas została przeszkolona do zakładania, że ​​nasz kod będzie wykonywany sekwencyjnie. W przypadku większości wielowątkowości nie możemy tego uznać za rzecz oczywistą „po wyjęciu z pudełka”.

Morgan Herlocker
źródło
-3

Spróbuj użyć kilku młotów do zacierania kilku gwoździ blisko siebie, jednocześnie bez komunikacji między tymi, którzy trzymają młoty ... (zakładając, że mają zasłonięte oczy).

Eskaluj to do budowy domu.

Teraz możesz spać w nocy wyobrażając sobie, że jesteś architektem. :)

Macke
źródło
-3

Prosta część: używaj wielowątkowości ze współczesnymi funkcjami frameworków, systemów operacyjnych i sprzętu, takimi jak semafory, kolejki, liczniki blokowane, typy pudełek atomowych itp.

Część trudna: zaimplementuj funkcje samodzielnie, nie używając żadnych funkcji na pierwszym miejscu, może być tylko kilka bardzo ograniczonych możliwości sprzętowych, opierając się powiedzmy tylko na gwarancjach spójności taktowania na wielu rdzeniach.


źródło
3
Trudna część jest rzeczywiście trudniejsza, ale nawet ta łatwa część nie jest taka łatwa.
PeterAllenWebb