Czego nauczyłeś się z projektu, który prawie / właściwie zawiódł z powodu złej wielowątkowości?
Czasami framework narzuca pewien model wątkowania, który sprawia, że trudniej jest uzyskać porządek o rząd wielkości.
Jeśli chodzi o mnie, muszę jeszcze wyjść z ostatniej porażki i uważam, że lepiej dla mnie nie pracować nad niczym, co ma związek z wielowątkowością w tych ramach.
Przekonałem się, że byłem dobry w problemach z wielowątkowością, które mają proste rozwidlenie / łączenie i gdzie dane przemieszczają się tylko w jednym kierunku (podczas gdy sygnały mogą przemieszczać się w kierunku kołowym).
Nie jestem w stanie obsłużyć GUI, w którym część pracy można wykonać tylko na ściśle serializowanym wątku („główny wątek”), a inną pracę można wykonać tylko na dowolnym wątku oprócz głównego wątku („wątki robocze”), oraz gdzie dane i komunikaty muszą przemieszczać się we wszystkich kierunkach między N składowymi (w pełni połączony wykres).
W momencie, gdy zostawiłem ten projekt na inny, wszędzie występowały problemy z impasem. Słyszałem, że 2-3 miesiące później kilku innym programistom udało się naprawić wszystkie problemy z impasem do tego stopnia, że można je wysłać do klientów. Nigdy nie udało mi się znaleźć tej brakującej wiedzy, której mi brakuje.
Coś w projekcie: liczba identyfikatorów wiadomości (wartości całkowite opisujące znaczenie zdarzenia, które można wysłać do kolejki komunikatów innego obiektu, niezależnie od wątków) wynosi kilka tysięcy. Unikalne ciągi (wiadomości użytkownika) również mają około tysiąca.
Dodany
Najlepszą analogią, jaką otrzymałem od innego zespołu (niezwiązaną z moimi wcześniejszymi lub obecnymi projektami), było „umieszczenie danych w bazie danych”. („Baza danych” odnosząca się do centralizacji i aktualizacji atomowych.) W graficznym interfejsie użytkownika podzielonym na wiele widoków, wszystkie działające w tym samym „głównym wątku”, a wszystkie operacje podnoszenia ciężarów innych niż GUI są wykonywane w poszczególnych wątkach roboczych, dane aplikacji powinny być przechowywane w jednym pliku, który działa jak baza danych, i pozwól „bazie danych” obsłużyć wszystkie „aktualizacje atomowe” obejmujące nietrywialne zależności danych. Wszystkie pozostałe części GUI obsługują tylko rysowanie ekranu i nic więcej. Części interfejsu użytkownika mogą buforować pliki, a użytkownik nie zauważy, czy są ułamkowe przez ułamek sekundy, jeśli są odpowiednio zaprojektowane. Ta „baza danych” jest również znana jako „dokument” w architekturze Document-View. Niestety - nie, moja aplikacja faktycznie przechowuje wszystkie dane w widokach. Nie wiem, dlaczego tak było.
Współtwórcy:
(autorzy nie muszą używać prawdziwych / osobistych przykładów. Mile widziane są również wnioski z niepotwierdzonych przykładów, jeśli sam ocenisz je jako wiarygodne).
Odpowiedzi:
Moja ulubiona lekcja - bardzo ciężko wygrana! - jest to, że w programie wielowątkowym program planujący jest podstępną świnią, która cię nienawidzi. Jeśli coś pójdzie nie tak, zrobią to, ale w nieoczekiwany sposób. Zrozum cokolwiek złego, a będziesz ścigać dziwne błędy heisen-bugs (ponieważ każda dodana instrumentacja zmieni taktowanie i da ci inny wzorzec przebiegu).
Jedynym rozsądnym sposobem na rozwiązanie tego problemu jest ścisłe przekierowanie obsługi wątków na niewielki fragment kodu, który zapewnia prawidłowe działanie i który jest bardzo konserwatywny pod względem zapewniania, że blokady są odpowiednio trzymane (i przy globalnie stałej kolejności pozyskiwania) . Najłatwiej to zrobić, nie współużytkując pamięci (lub innych zasobów) między wątkami, z wyjątkiem komunikatów, które muszą być asynchroniczne; który pozwala pisać wszystko inne w stylu, który nie uwzględnia wątków. (Bonus: skalowanie do wielu komputerów w klastrze jest znacznie łatwiejsze).
źródło
is that in a multithreaded program the scheduler is a sneaky swine that hates you.
- nie, nie robi, robi dokładnie to, coOto kilka podstawowych lekcji, o których mogę teraz pomyśleć (nie z projektów zakończonych niepowodzeniem, ale z rzeczywistych problemów widocznych w rzeczywistych projektach):
źródło
Odziedziczyliśmy część, w której projekt GUI wykorzystuje tuzin wątków. Daje tylko problemy. Zakleszczenia, problemy z wyścigami, połączenia GUI między wątkami ...
źródło
Java 5 i nowsze wersje mają moduły wykonawcze, które mają ułatwić życie w obsłudze wielowątkowych programów w stylu łączenia widelca.
Używaj ich, to usunie wiele bólu.
(i tak, nauczyłem się z projektu :))
źródło
Mam doświadczenie w twardych systemach osadzonych w czasie rzeczywistym. Nie można testować pod kątem braku problemów spowodowanych wielowątkowością. (Czasami możesz potwierdzić obecność). Kod musi być możliwy do udowodnienia. Tak więc najlepsza praktyka dotycząca dowolnej interakcji wątków.
źródło
Bardzo pomocna była analogia z lekcji wielowątkowości, którą wziąłem w zeszłym roku. Synchronizacja wątków jest jak sygnał drogowy chroniący skrzyżowanie (dane) przed użyciem przez dwa samochody (wątki) jednocześnie. Błędem wielu programistów jest zmienianie świateł na czerwono w większości miast, aby przepuścić jeden samochód, ponieważ uważają, że zbyt trudno lub niebezpiecznie jest ustalić dokładny sygnał, którego potrzebują. Może to działać dobrze, gdy ruch jest mały, ale doprowadzi do blokowania w miarę wzrostu aplikacji.
Jest to coś, co już znałem teoretycznie, ale po tej klasie analogia naprawdę się ze mną utknęła i byłem zdumiony, jak często potem badałem problem z wątkami i znajdowałem jedną wielką kolejkę lub przerywałem wyłączanie wszędzie podczas zapisu do zmiennej tylko dwa używane wątki lub muteksy są trzymane przez długi czas, kiedy można je zrefaktoryzować, aby całkowicie tego uniknąć.
Innymi słowy, niektóre z najgorszych problemów z wątkami są powodowane przez nadmierną liczbę prób uniknięcia problemów z wątkami.
źródło
Spróbuj zrobić to jeszcze raz.
Przynajmniej dla mnie różnicą była praktyka. Po kilkukrotnym wykonaniu pracy wielowątkowej i rozproszonej po prostu się zorientujesz.
Myślę, że debugowanie jest naprawdę tym, co utrudnia. Mogę debugować wielowątkowy kod za pomocą VS, ale naprawdę mam całkowitą stratę, jeśli muszę użyć gdb. Prawdopodobnie moja wina.
Kolejną rzeczą, o której dowiadujemy się więcej, jest struktura danych bez blokady.
Myślę, że to pytanie można naprawdę poprawić, jeśli określisz ramy. Na przykład pule wątków i procesy działające w tle .NET naprawdę różnią się od QThread. Zawsze jest kilka gotowych specyficznych dla platformy.
źródło
Nauczyłem się, że wywołania zwrotne z modułów niższego poziomu do modułów wyższego poziomu są wielkim złem, ponieważ powodują nabywanie blokad w odwrotnej kolejności.
źródło