Współbieżność: jak podchodzisz do projektu i debugujesz implementację?

37

Od kilku lat rozwijam systemy współbieżne i całkiem dobrze rozumiem ten temat, pomimo braku formalnego wykształcenia (tj. Bez dyplomu). Istnieje kilka nowych języków, o których ostatnio ostatnio mówi się, a które mają ułatwić współbieżność, takich jak Erlang i Go. Wygląda na to, że ich podejście do współbieżności odzwierciedla moje własne doświadczenie w zakresie skalowalności systemów i korzystania z wielu rdzeni / procesorów / maszyn.

Uważam jednak, że istnieje bardzo niewiele narzędzi, które pomagają zwizualizować to, co zamierzasz zrobić, i zweryfikować, czy jesteś przynajmniej bliski swojej pierwotnej wizji. Debugowanie współbieżnego kodu może być koszmarem w językach, które nie są zaprojektowane dla współbieżności (takich jak C / C ++, C #, Java itp.). W szczególności odtworzenie warunków, które występują w jednym systemie w środowisku programistycznym, może być prawie niemożliwe.

Jakie są twoje podejścia do projektowania systemu do obsługi współbieżności i przetwarzania równoległego? Przykłady:

  • Jak wymyślić, co można uczynić współbieżnym z tym, co musi być sekwencyjne?
  • Jak odtworzyć warunki błędu i zobaczyć, co dzieje się podczas działania aplikacji?
  • Jak wizualizujesz interakcje między różnymi współbieżnymi częściami aplikacji?

Mam odpowiedzi na niektóre z nich, ale chciałbym również dowiedzieć się więcej.

Edytować

Jak dotąd mamy dużo dobrego wkładu. Wiele artykułów z linkami jest bardzo dobrych, a niektóre z nich już przeczytałem.

Moje osobiste doświadczenie z programowaniem równoległym prowadzi mnie do wniosku, że potrzebujesz innego sposobu myślenia niż programowanie sekwencyjne. Podział mentalny jest prawdopodobnie tak szeroki, jak różnica między programowaniem obiektowym a programowaniem proceduralnym. Chciałbym, aby ten zestaw pytań koncentrował się bardziej na procesach myślowych niezbędnych (tj. Teorii), aby systematycznie podchodzić do odpowiedzi. Podając bardziej konkretne odpowiedzi, możesz podać przykład - coś, przez co przeszedłeś osobiście.

Cel nagrody

Nie mów mi, co powinienem zrobić. Już to mam pod kontrolą. Powiedz mi co robisz. Powiedz mi, jak Ci rozwiązać te problemy.

Berin Loritsch
źródło
To dobre pytanie - wiele możliwych głębokości. Mam również dobre doświadczenia z aplikacjami wielowątkowymi w Javie, ale chciałbym dowiedzieć się więcej.
Michael K,
Do tej pory mamy kilka dobrych odpowiedzi. Ktoś chce rzucić wyzwanie temu, co chciałbyś pomóc w tej dziedzinie?
Berin Loritsch,
Debiutant TotalView do współbieżnego kodowania jest dość przydatnym narzędziem, wymaga jednak nieco krzywej uczenia się - totalviewtech.com/products/totalview.html
Fanatic23
Może logowanie może pomóc w dwóch ostatnich pytaniach.
Amir Rezaei,
To, czego szukam, to procesy ludzi. Są to obszary, w których narzędzia, których używałem, są nieodpowiednie, ale mogą wykonać zadanie. Nie martwię się o cytowanie czyichś artykułów, a bardziej o metodologię tutaj.
Berin Loritsch,

Odpowiedzi:

11

Od kilku lat rozwijam równoległe systemy i całkiem dobrze rozumiem ten temat, pomimo braku formalnego wykształcenia (tj. Bez dyplomu).

Wielu najlepszych programistów, których znam, nie ukończyło uniwersytetu. Jeśli chodzi o mnie, studiowałem filozofię.

C / C ++, C #, Java itp.). W szczególności odtworzenie warunków, które występują w jednym systemie w środowisku programistycznym, może być prawie niemożliwe.

tak

Jak wymyślić, co można uczynić współbieżnym z tym, co musi być sekwencyjne?

zwykle zaczynamy od metafory o wysokości 1000 mil, aby wyjaśnić naszą architekturę sobie (po pierwsze) i innym (po drugie).

Kiedy napotkaliśmy ten problem, zawsze znajdowaliśmy sposób na ograniczenie widoczności współbieżnych obiektów do niejednorodnych.

Ostatnio odkryłem Aktorów w Scali i zobaczyłem, że moje stare rozwiązania były rodzajem „miniaktorów”, znacznie mniej wydajnych niż Scala. Więc proponuję zacząć od tego.

Inną sugestią jest pominięcie jak największej liczby problemów: na przykład używamy scentralizowanej pamięci podręcznej (terakoty) zamiast utrzymywania map w pamięci, używając wywołań zwrotnych klasy wewnętrznej zamiast metod zsynchronizowanych, wysyłania wiadomości zamiast pisania pamięci współdzielonej itp.

W Scali i tak wszystko jest znacznie łatwiejsze.

Jak odtworzyć warunki błędu i zobaczyć, co dzieje się podczas działania aplikacji?

Nie ma tu prawdziwej odpowiedzi. Mamy kilka testów jednostkowych pod kątem współbieżności i pakiet testów obciążenia, aby maksymalnie obciążać aplikację.

Jak wizualizujesz interakcje między różnymi współbieżnymi częściami aplikacji?

Ponownie nie ma prawdziwej odpowiedzi: projektujemy naszą Metaforę na tablicy i staramy się upewnić, że nie ma konfliktów po stronie architektonicznej.

Dla Arch tutaj mam na myśli definicję Neal Forda: Sw Architecture to wszystko, co później będzie bardzo trudne do zmiany.

programowanie prowadzi mnie do przekonania, że ​​potrzebujesz innego sposobu myślenia niż w przypadku programowania sekwencyjnego.

Być może, ale dla mnie po prostu niemożliwe jest myślenie równolegle, więc lepiej zaprojektuj nasze oprogramowanie w sposób, który nie wymaga równoległego myślenia i z wyraźnymi barierami ochronnymi, aby uniknąć awarii między liniami współbieżności.

Uberto
źródło
6

Dla mnie wszystko dotyczy danych. Popraw swoje dane, a równoległe przetwarzanie jest łatwe. Wszystkie problemy z retencją, impasami i tak znikają.

Wiem, że nie jest to jedyny sposób na zrównoleglenie, ale dla mnie jest zdecydowanie najbardziej użyteczny.

Dla zilustrowania (niezbyt szybka) historia:

Pracowałem nad dużym systemem finansowym (kontrola rynku akcji) w latach 2007-2009, a wielkość przetwarzania danych była bardzo duża. Aby to zilustrować, wszystkie obliczenia przeprowadzone na 1 pojedynczym koncie klienta zajęły około 1 ~ 3 sekundy na ich przeciętnej stacji roboczej, a kont było ponad 30 000. Każdej nocy zamykanie systemu było dużym problemem dla użytkowników (zwykle trwało ponad 6 godzin, bez marginesu błędów).

Badanie problemu wykazało, że możemy sparaliżować obliczenia między kilkoma komputerami, ale nadal mamy ogromne wąskie gardło na starym serwerze bazy danych (serwer SQL 2000 emulujący SQL 6.5).

Było całkiem jasne, że naszym minimalnym pakietem przetwarzania było obliczenie pojedynczego konta, a głównym wąskim gardłem było zatrzymywanie serwera bazy danych (mogliśmy zobaczyć na kilku połączeniach „sp_who” oczekujących na takie samo przetwarzanie). Proces równoległy przebiegał następująco:

1) Jeden pojedynczy producent, odpowiedzialny za czytanie bazy danych lub pisanie na niej sekwencyjnie. Niedozwolona jest tutaj współbieżność. Producent przygotował dla konsumentów kolejkę zleceń. Baza danych należała wyłącznie do tego producenta.

2) Kilku konsumentów na kilku komputerach. Każdy z klientów otrzymał z kolejki cały pakiet danych, gotowy do obliczeń. Każda operacja wymagająca jest zsynchronizowana.

3) Po obliczeniu każdy konsument odesłał dane do kolejki synchronizowanej w pamięci do producenta, aby zachować dane.

Było kilka punktów kontrolnych, kilka mechanizmów zapewniających prawidłowe zapisanie transakcji (żadnego nie pozostawiono), ale cała praca była tego warta. Ostatecznie obliczenia rozłożone na 10 komputerów (plus komputer producenta / kolejki) skróciły czas zamknięcia całego systemu do 15 minut.

Usunięcie problemów z retencją spowodowanych złym zarządzaniem współbieżnością SQL 6.5 dało nam dużą przewagę. Reszta była prawie liniowa, każdy nowy komputer dodany do „siatki” skracał czas przetwarzania, aż do osiągnięcia „maksymalnej wydajności” sekwencyjnych operacji odczytu / zapisu w bazie danych.

Machado
źródło
2

Praca w środowisku wielowątkowym jest trudna i wymaga dyscypliny kodowania. Musisz postępować zgodnie z odpowiednimi wytycznymi dotyczącymi blokowania, zwalniania blokady, uzyskiwania dostępu do zmiennych globalnych itp.

Pozwól, że spróbuję odpowiedzieć na twoje pytanie jeden do jednego

* How do you figure out what can be made concurrent vs. what has to be sequential?

Użyj współbieżności dla

1) Sondowanie: - potrzebujesz wątku, aby ciągle odpytywać lub regularnie wysyłać aktualizacje. (Pojęcia takie jak bicie serca, które wysyłają pewne dane w regularnych odstępach czasu do centralnego serwera, aby powiedzieć, że żyję.)

2) Operacje, które mają ciężkie operacje we / wy można wykonać równolegle. Najlepszym przykładem jest rejestrator. Wątek programu rejestrującego może być osobnym wątkiem.

3) Podobne zadania na różnych danych. Jeśli jest jakieś zadanie, które dzieje się na różnych danych, ale ma bardzo podobny charakter, mogą to zrobić różne wątki. Najlepszym przykładem będą żądania serwera.

I oczywiście wiele innych lubi to w zależności od zastosowania.

* How do you reproduce error conditions and view what is happening as the application executes?

Korzystanie z dzienników i drukowanie debugowania w dziennikach. Spróbuj zalogować także identyfikator wątku, aby zobaczyć, co dzieje się w każdym wątku.
Jednym ze sposobów wygenerowania warunku błędu jest umieszczenie celowego opóźnienia (w kodzie debugowania) w miejscach, w których Twoim zdaniem ma miejsce problem, i wymuszenie zatrzymania tego wątku. Podobne rzeczy można zrobić również w debuggerach, ale do tej pory tego nie robiłem.

* How do you visualize the interactions between the different concurrent parts of the application?

Umieść dzienniki w swoich zamkach, abyś wiedział, kto i co blokuje, a kto próbował. Jak powiedziałem wcześniej, spróbuj umieścić identyfikator wątku w dzienniku, aby zrozumieć, co się dzieje w każdym wątku.

To tylko moja rada, która dotyczy około 3 lat pracy nad aplikacją wielowątkową i mam nadzieję, że pomoże.

Manoj R.
źródło
2
  • Jak wymyślić, co można uczynić współbieżnym z tym, co musi być sekwencyjne?

Najpierw zapytałbym, czy aplikacja (lub komponent) rzeczywiście skorzysta na równoczesnym przetwarzaniu, czy w kategoriach laika - gdzie jest wąskie gardło? Współbieżność oczywiście nie zawsze zapewni korzyść dla inwestycji, której wymaga, aby działała. Jeśli wygląda jak kandydat, wtedy pracowałbym oddolnie - próbując znaleźć największą operację lub zestaw operacji, które mogą skutecznie wykonywać swoją pracę w izolacji - nie chcę rozwijać wątków dla nieistotnych, nieefektywnych kosztowo operacje - szukam aktorów .

Pracując z Erlangiem, uwielbiam koncepcję używania asynchronicznego przekazywania wiadomości i modelu aktora dla współbieżności - jest intuicyjny, skuteczny i czysty.

Off of Understanding Aktor współbieżności

Model aktora składa się z kilku kluczowych zasad:

  • Brak stanu wspólnego
  • Lekkie procesy
  • Asynchroniczne przekazywanie wiadomości
  • Skrzynki pocztowe do buforowania wiadomości przychodzących
  • Przetwarzanie skrzynki pocztowej z dopasowaniem wzorca

Aktor to proces, który wykonuje funkcję. Tutaj proces jest lekkim wątkiem przestrzeni użytkownika (nie należy go mylić z typowym ciężkim procesem systemu operacyjnego). Aktorzy nigdy nie dzielą stanu i dlatego nigdy nie muszą rywalizować o blokady dostępu do wspólnych danych. Zamiast tego aktorzy współużytkują dane, wysyłając niezmienne komunikaty. Niezmiennych danych nie można modyfikować, więc odczyty nie wymagają blokady.

Model współbieżności Erlanga jest łatwiejszy do zrozumienia i debugowania niż blokowanie i współdzielenie danych. Sposób izolowania logiki ułatwia testowanie komponentów poprzez przekazywanie im komunikatów.

Pracując z systemami współbieżnymi, tak i tak działał mój projekt w każdym języku - kolejka, z której wiele wątków pobierałoby dane, wykonywała prostą operację i powtarzała lub wypychała z powrotem do kolejki. Erlang tylko wymusza niezmienne struktury danych, aby zapobiec skutkom ubocznym i zmniejszyć koszty i złożoność tworzenia nowych wątków.

Ten model nie jest wyłączny dla Erlanga, nawet w świecie Java i .NET istnieją sposoby na jego utworzenie - przyjrzałbym się programom współbieżności i koordynacji (CCR) i Relangowi (istnieje również Jetlang dla Javy).

  • Jak odtworzyć warunki błędu i zobaczyć, co dzieje się podczas działania aplikacji?

Z mojego doświadczenia wynika, że ​​jedyne, co mogłem zrobić, to zobowiązanie do śledzenia / rejestrowania wszystkiego. Każdy proces / wątek musi mieć identyfikator, a każda nowa jednostka pracy musi mieć identyfikator korelacji. Musisz być w stanie przejrzeć swoje dzienniki i dokładnie prześledzić, co było przetwarzane i kiedy - nie widziałem magii, która by to wyeliminowała.

  • Jak wizualizujesz interakcje między różnymi współbieżnymi częściami aplikacji?

Patrz wyżej, jest brzydka, ale działa. Jedyne, co robię, to korzystać ze schematów sekwencji UML - oczywiście w czasie projektowania - ale możesz ich użyć, aby sprawdzić, czy twoje komponenty mówią tak, jak chcesz.

Watson
źródło
1

- Moje odpowiedzi dotyczą MS / Visual Studio -

Jak wymyślić, co można uczynić współbieżnym z tym, co musi być sekwencyjne?

To wymaga wiedzy w dziedzinie, nie będzie tu żadnego ogólnego oświadczenia, które by to obejmowało.

Jak odtworzyć warunki błędu i zobaczyć, co dzieje się podczas działania aplikacji?

Dużo rejestrowania, możliwość włączenia / wyłączenia / podniesienia rejestrowania w aplikacjach produkcyjnych w celu złapania go w produkcji. VS2010 Intellitrace ma być w stanie w tym pomóc, ale jeszcze go nie użyłem.

Jak wizualizujesz interakcje między różnymi współbieżnymi częściami aplikacji?

Nie mam na to dobrej odpowiedzi, chciałbym ją zobaczyć.

Czarny lód
źródło
Rejestrowanie zmieni sposób wykonywania kodu, a tym samym może prowadzić do błędu, którego się nie wyświetlasz.
Matthew
1

Nie zgadzam się z twoim stwierdzeniem, że C nie jest przeznaczony do współbieżności. C jest przeznaczony do ogólnego programowania systemów i chętnie wskazuje krytyczne decyzje, które należy podjąć, i będzie to robił przez wiele lat. Dzieje się tak nawet wtedy, gdy najlepszą decyzją może być nie używanie C. Dodatkowo, współbieżność w C jest tak trudna, jak skomplikowany projekt.

Staram się, najlepiej jak potrafię, wdrożyć blokady z myślą, że w końcu naprawdę praktyczne programowanie bez blokady może stać się dla mnie rzeczywistością. Blokując, nie mam na myśli wzajemnego wykluczenia, mam na myśli proces, który wdraża bezpieczną współbieżność bez konieczności arbitrażu. Przez praktyczne rozumiem coś, co jest łatwiejsze do przeniesienia niż do wdrożenia. Mam również bardzo mało formalnego treningu CS, ale przypuszczam, że wolno mi to życzyć :)

Następnie większość napotkanych przeze mnie robaków staje się stosunkowo płytka, lub tak zupełnie zadziwiająca, że ​​wycofuję się do pubu. Pub staje się atrakcyjną opcją tylko wtedy, gdy profilowanie programu wystarczająco go spowalnia, aby odsłonić dodatkowe rasy, które nie są związane z tym, co próbuję znaleźć.

Jak zauważyli inni, opisany przez ciebie problem jest wyjątkowo specyficzny dla domeny. Po prostu staram się, najlepiej jak potrafię, unikać wszelkich spraw, które mogą wymagać arbitrażu (poza moim procesem), gdy tylko jest to możliwe. Jeśli to wygląda na królewski ból, ponownie oceniam opcję nadania wielu wątkom lub procesom równoczesnego i nieserializowanego dostępu do czegoś.

Z drugiej strony wrzuć tam „rozproszony” i arbitraż staje się koniecznością. Czy masz konkretny przykład?

Tim Post
źródło
Aby wyjaśnić moje stwierdzenie, C nie został zaprojektowany specjalnie dla współbieżności i wokół niej. Jest to w przeciwieństwie do języków takich jak Go, Erlang i Scala, które zostały zaprojektowane wyraźnie z myślą o współbieżności. Nie miałem zamiaru powiedzieć, że nie możesz robić współbieżności z C.
Berin Loritsch
1

Jak odtworzyć warunki błędu i zobaczyć, co dzieje się podczas działania aplikacji?

Jak wizualizujesz interakcje między różnymi współbieżnymi częściami aplikacji?

Z mojego doświadczenia wynika, że ​​odpowiedzi na te dwa aspekty są następujące:

Rozproszone śledzenie

Rozproszone śledzenie to technologia, która przechwytuje dane czasowe dla poszczególnych współbieżnych elementów systemu i przedstawia je w formacie graficznym. Reprezentacje jednoczesnych wykonań są zawsze przeplatane, co pozwala zobaczyć, co działa równolegle, a co nie.

Rozproszone śledzenie ma swoje początki w (oczywiście) systemach rozproszonych, które z definicji są asynchroniczne i wysoce współbieżne. Rozproszony system z rozproszonym śledzeniem umożliwia ludziom:

a) zidentyfikuj ważne wąskie gardła, b) uzyskaj wizualną reprezentację idealnych „uruchomień” twojej aplikacji, oraz c) zapewnij wgląd w to, jakie współbieżne zachowanie jest wykonywane, d) uzyskaj dane czasowe, które można wykorzystać do oceny różnic między zmianami w twojej system (bardzo ważne, jeśli masz silne umowy SLA).

Konsekwencje rozproszonego śledzenia są jednak:

  1. Dodaje to narzut do wszystkich współbieżnych procesów, ponieważ przekłada się na więcej kodu do wykonania i przesłania potencjalnie przez sieć. W niektórych przypadkach obciążenie to jest bardzo znaczące - nawet Google używa systemu śledzenia Dapper tylko w niewielkim podzbiorze wszystkich żądań, aby nie zniszczyć doświadczenia użytkownika.

  2. Istnieje wiele różnych narzędzi, z których nie wszystkie są interoperacyjne. Jest to nieco poprawione przez standardy takie jak OpenTracing, ale nie w pełni rozwiązane.

  3. Nie mówi nic o zasobach udostępnionych i ich bieżącym statusie. Możesz zgadnąć na podstawie kodu aplikacji i tego, co pokazuje wykres, ale nie jest to przydatne narzędzie w tym zakresie.

  4. Obecne narzędzia zakładają, że masz do dyspozycji pamięć i pamięć. Hostowanie serwera timeseries może nie być tanie, w zależności od twoich ograniczeń.

Oprogramowanie do śledzenia błędów

Łączę do Sentry powyżej przede wszystkim dlatego, że jest to najczęściej używane narzędzie, i nie bez powodu - oprogramowanie do śledzenia błędów, takie jak wykonywanie środowiska wykonawczego Sentry, do jednoczesnego przesyłania śladu błędów napotkanych błędów na centralny serwer.

Korzyści netto z takiego dedykowanego oprogramowania w współbieżnym kodzie:

  1. Zduplikowane błędy nie są duplikowane . Innymi słowy, jeśli jeden lub więcej współbieżnych systemów napotka ten sam wyjątek, Sentry zwiększy raport o incydencie, ale nie prześle dwóch kopii incydentu.

Oznacza to, że możesz dowiedzieć się, który system współbieży napotyka dany rodzaj błędu, bez konieczności przechodzenia przez niezliczoną liczbę równoczesnych raportów o błędach. Jeśli kiedykolwiek spotkałeś się z spamem pochodzącym z rozproszonego systemu, wiesz, jak się czuje piekło.

Możesz nawet „otagować” różne aspekty systemu współbieżnego (chociaż zakłada to, że nie przeplatasz pracy dokładnie nad jednym wątkiem, co technicznie i tak nie jest zbieżne, ponieważ wątek po prostu skutecznie przeskakuje między zadaniami, ale nadal musi przetwarzać procedury obsługi zdarzeń do ukończenia) i zobacz rozkład błędów według tagu.

  1. Możesz zmodyfikować to oprogramowanie do obsługi błędów, aby podać dodatkowe szczegóły dotyczące wyjątków czasu wykonywania. Jakie otwarte zasoby miał proces? Czy istnieje współużytkowany zasób przechowywany przez ten proces? Który użytkownik doświadczył tego problemu?

To, oprócz drobiazgowych śladów stosu (i map źródłowych, jeśli musisz dostarczyć zminimalizowaną wersję swoich plików), ułatwia ustalenie, co dzieje się źle przez większą część czasu.

  1. (Specyficzne dla Sentry) Możesz mieć osobny pulpit raportowania Sentry dla testów uruchomieniowych systemu, co pozwala wychwytywać błędy podczas testowania.

Wady takiego oprogramowania obejmują:

  1. Jak wszystko, dodają luzem. Na przykład możesz nie chcieć takiego systemu na sprzęcie wbudowanym. Zdecydowanie zalecam wykonanie próbnego uruchomienia takiego oprogramowania, porównując proste wykonanie z próbką i bez niej w kilkuset uruchomieniach na bezczynnej maszynie.

  2. Nie wszystkie języki są jednakowo obsługiwane, ponieważ wiele z tych systemów polega na domniemanym wyłapywaniu wyjątku i nie wszystkie języki mają solidne wyjątki. To powiedziawszy, są klienci dla wielu systemów.

  3. Mogą być podnoszone jako zagrożenie bezpieczeństwa, ponieważ wiele z tych systemów jest zasadniczo zamkniętych źródeł. W takich przypadkach dołóż należytej staranności, by je zbadać lub, jeśli wolisz, rzuć własną.

  4. Nie zawsze dają ci potrzebne informacje. Jest to ryzyko przy wszystkich próbach zwiększenia widoczności.

  5. Większość z tych usług została zaprojektowana dla wysoce współbieżnych aplikacji internetowych, więc nie każde narzędzie może być idealne dla twojego przypadku użycia.

Podsumowując : widoczność jest najważniejszą częścią każdego współbieżnego systemu. Dwie metody, które opisałem powyżej, w połączeniu z dedykowanymi pulpitami nawigacyjnymi na temat sprzętu i danych w celu uzyskania holidetycznego obrazu systemu w dowolnym punkcie czasowym, są szeroko stosowane w branży właśnie w celu rozwiązania tego aspektu.

Kilka dodatkowych sugestii

Spędziłem więcej czasu niż zależy mi na poprawianiu kodu przez ludzi, którzy próbowali rozwiązać współbieżne problemy w okropny sposób. Za każdym razem znajdowałem przypadki, w których następujące rzeczy mogą znacznie poprawić wrażenia programistów (co jest równie ważne jak wrażenia użytkownika):

  • Polegaj na typach . Wpisanie istnieje w celu sprawdzenia poprawności kodu i może być używane w czasie wykonywania jako dodatkowa ochrona. Tam, gdzie pisanie nie istnieje, polegaj na stwierdzeniach i odpowiedniej procedurze obsługi błędów, aby wychwycić błędy. Współbieżny kod wymaga kodu obronnego, a typy służą jako najlepszy dostępny sposób sprawdzania poprawności.

    • Testuj połączenia między składnikami kodu , a nie tylko samym składnikiem. Nie należy tego mylić z pełnoprawnym testem integracyjnym - który testuje każde łącze między każdym komponentem, a nawet wtedy szuka tylko globalnej weryfikacji ostatecznego stanu. To okropny sposób na wyłapywanie błędów.

Dobry test linku sprawdza, czy kiedy jeden komponent rozmawia w izolacji z innym komponentem , otrzymana wiadomość i wysłana wiadomość są takie same, jak się spodziewasz. Jeśli masz dwa lub więcej składników polegających na wspólnej usłudze do komunikacji, rozłącz je wszystkie, poproś, aby wymieniły wiadomości za pośrednictwem usługi centralnej i sprawdź, czy wszystkie dostają to, czego oczekujesz w końcu.

Rozbijanie testów obejmujących wiele komponentów na test samych komponentów i test na to, jak również komunikują się poszczególne komponenty, daje większą pewność co do poprawności kodu. Mając tak rygorystyczny zestaw testów, możesz egzekwować umowy między usługami, a także wychwytywać nieoczekiwane błędy, które pojawiają się, gdy są uruchomione jednocześnie.

  • Użyj odpowiednich algorytmów, aby sprawdzić poprawność stanu aplikacji. Mówię o prostych rzeczach, takich jak proces nadrzędny, który czeka na wszystkich pracowników, aby ukończyli zadanie, i chcę przejść do następnego kroku, jeśli wszyscy pracownicy są w pełni skończeni - jest to przykład wykrywania globalnego zakończenie, dla którego istnieją znane metodologie, takie jak algorytm Safry.

Niektóre z tych narzędzi są dostarczane w pakiecie z językami - na przykład Rust gwarantuje, że Twój kod nie będzie miał warunków wyścigu w czasie kompilacji, podczas gdy Go ma wbudowany wykrywacz zakleszczeń, który działa również w czasie kompilacji. Jeśli potrafisz wychwycić problemy, zanim trafią do produkcji, zawsze jest to wygrana.

Ogólna ogólna zasada: projektowanie awarii w systemach współbieżnych . Przewiduj, że wspólne usługi ulegną awarii lub pękną. Dotyczy to nawet kodu, który nie jest dystrybuowany między komputerami - współbieżny kod na jednym komputerze może polegać na zewnętrznych zależnościach (takich jak wspólny plik dziennika, serwer Redis, cholerny serwer MySQL), które mogą zniknąć lub zostać usunięte w dowolnym momencie .

Najlepszym sposobem na to jest sprawdzanie stanu aplikacji od czasu do czasu - sprawdzanie stanu każdej usługi i upewnianie się, że konsumenci tej usługi są powiadamiani o złym stanie zdrowia. Nowoczesne narzędzia kontenerowe, takie jak Docker, robią to całkiem dobrze i powinny być używane do piaskownicy.

Jak wymyślić, co można uczynić współbieżnym, a co sekwencyjnym?

Jedną z największych lekcji, których nauczyłem się podczas pracy nad wysoce współbieżnym systemem, jest to: nigdy nie możesz mieć wystarczającej liczby wskaźników . Metryki powinny napędzać absolutnie wszystko w twojej aplikacji - nie jesteś inżynierem, jeśli nie mierzysz wszystkiego.

Bez wskaźników nie można zrobić kilku bardzo ważnych rzeczy:

  1. Oceń różnicę wprowadzoną przez zmiany w systemie. Jeśli nie wiesz, czy pokrętło strojenia A sprawiło, że metryka B wzrosła, a metryka C obniżyła się, nie wiesz, jak naprawić system, gdy ludzie pchają nieoczekiwanie złośliwy kod w twoim systemie (i będą pchać kod do twojego systemu) .

  2. Dowiedz się, co musisz zrobić, aby poprawić. Dopóki nie dowiesz się, że w aplikacjach zaczyna brakować pamięci, nie możesz stwierdzić, czy powinieneś uzyskać więcej pamięci, czy kupić więcej dysku dla swoich serwerów.

Miary są tak ważne i niezbędne, że podjąłem świadomy wysiłek, aby zaplanować to, co chcę zmierzyć, zanim nawet zastanowię się, czego będzie wymagał system. W rzeczywistości metryki są tak ważne, że uważam, że są właściwą odpowiedzią na to pytanie: wiesz tylko, co można uczynić sekwencyjnym lub współbieżnym, gdy mierzysz, co robią bity twojego programu. Właściwa konstrukcja wykorzystuje liczby, a nie zgadywanie.

Biorąc to pod uwagę, z pewnością istnieje kilka podstawowych zasad:

  1. Sekwencyjny oznacza zależność. Dwa procesy powinny być sekwencyjne, jeśli jeden jest w pewien sposób zależny od drugiego. Procesy bez zależności powinny być współbieżne. Zaplanuj jednak sposób radzenia sobie z awarią w górę strumienia, która nie uniemożliwia procesom w dalszym ciągu oczekiwania na czas nieokreślony.

  2. Nigdy nie należy mieszać zadania związanego z operacjami we / wy z zadaniem związanym z procesorem na tym samym rdzeniu. Nie pisz (na przykład) przeszukiwacza sieci, który uruchamia dziesięć równoczesnych żądań w tym samym wątku, zgarnia je, gdy tylko się pojawią, i spodziewaj się skalowania do pięciuset - żądania we / wy idą do kolejki równolegle, ale CPU nadal będzie je przechodził szeregowo. (Ten jednowątkowy model sterowany zdarzeniami jest popularny, ale jest ograniczony ze względu na ten aspekt - zamiast tego zrozumieć, ludzie po prostu załamują ręce i mówią, że Node nie skaluje się, aby dać ci przykład).

Pojedynczy wątek może wykonać wiele operacji we / wy. Aby jednak w pełni wykorzystać współbieżność sprzętu, użyj pul wątków, które razem zajmują wszystkie rdzenie. W powyższym przykładzie uruchomienie pięciu procesów Pythona (z których każdy może korzystać z rdzenia na maszynie sześciordzeniowej) tylko do pracy z procesorem, a szósty wątek Pythona tylko do pracy we / wy będzie skalowany znacznie szybciej niż myślisz.

Jedynym sposobem na wykorzystanie współbieżności procesora jest dedykowana pula wątków. Pojedynczy wątek jest często wystarczający do wielu prac związanych z operacjami wejścia / wyjścia. Dlatego serwery sieciowe sterowane zdarzeniami, takie jak Nginx, lepiej skalują się (wykonują wyłącznie operacje związane z operacjami we / wy) niż Apache (które łączą pracę związaną z operacjami we / wy z czymś wymagającym procesora i uruchamiają proces na żądanie), ale po co używać węzła do działania dziesiątki tysięcy równoległych obliczeń GPU to okropny pomysł.

Akshat Mahajan
źródło
0

Cóż, w procesie weryfikacji, projektując duży równoległy system - zwykle testuję model za pomocą LTSA - Labeled Transition System Analyzer . Został on opracowany przez mojego starego nauczyciela, który jest weteranem w dziedzinie współbieżności i jest teraz szefem informatyki w Imperial.

Jeśli chodzi o ustalenie, co może być, a czego nie może być współbieżne, istnieją statyczne analizatory, które mogą to pokazać, jak sądzę, chociaż zwykle rysuję schematy harmonogramu dla krytycznych sekcji, tak samo jak w przypadku zarządzania projektem. Następnie zidentyfikuj sekcje, które powtarzalnie wykonują tę samą operację. Szybką trasą jest po prostu znalezienie pętli, ponieważ są to obszary, które korzystają z równoległego przetwarzania.

Orbling
źródło
0

Jak wymyślić, co można uczynić współbieżnym z tym, co musi być sekwencyjne?

Prawie każda rzecz, którą piszesz, może korzystać z współbieżności, zwłaszcza przypadek użycia „devide a conquer”. O wiele lepsze pytanie brzmi: co powinno być równoległe?

Joseph Albahari's Threading in C # wymienia pięć typowych zastosowań.

Wielowątkowość ma wiele zastosowań; Oto najczęstsze:

Utrzymanie responsywnego interfejsu użytkownika

Dzięki uruchamianiu czasochłonnych zadań w równoległym wątku „roboczym” główny wątek interfejsu użytkownika może nadal przetwarzać zdarzenia klawiatury i myszy.

Efektywne wykorzystanie zablokowanego procesora

Wielowątkowość jest przydatna, gdy wątek oczekuje na odpowiedź z innego komputera lub sprzętu. Podczas gdy jeden wątek jest blokowany podczas wykonywania zadania, inne wątki mogą korzystać z nieobciążonego komputera.

Programowanie równoległe

Kod, który wykonuje intensywne obliczenia, może działać szybciej na komputerach wielordzeniowych lub wieloprocesorowych, jeśli obciążenie jest dzielone między wiele wątków w strategii „dziel i rządź” (patrz Część 5).

Wykonanie spekulacyjne

Na maszynach wielordzeniowych możesz czasem poprawić wydajność, przewidując coś, co może wymagać zrobienia, a następnie robiąc to z wyprzedzeniem. LINQPad używa tej techniki, aby przyspieszyć tworzenie nowych zapytań. Wariant polega na równoległym uruchomieniu wielu różnych algorytmów, które rozwiązują to samo zadanie. W zależności od tego, który z nich zakończy jako pierwszy, „wygrywa” - jest to skuteczne, gdy nie możesz z góry wiedzieć, który algorytm wykona się najszybciej.

Zezwalanie na jednoczesne przetwarzanie żądań

Na serwerze żądania klientów mogą przychodzić jednocześnie i dlatego muszą być obsługiwane równolegle (.NET Framework tworzy do tego wątki automatycznie, jeśli używasz ASP.NET, WCF, usług internetowych lub usług zdalnych). Może to być również przydatne na kliencie (np. Obsługa sieci peer-to-peer - a nawet wielu żądań od użytkownika).

Jeśli nie próbujesz wykonać jednej z powyższych czynności, prawdopodobnie lepiej zastanów się nad tym.

Jak odtworzyć warunki błędu i zobaczyć, co dzieje się podczas działania aplikacji?

Jeśli używasz platformy .NET i masz zapisane przypadki użycia, możesz użyć funkcji CHESS, która może odtworzyć określone warunki przeplatania wątków, co pozwala przetestować poprawkę.

Jak wizualizujesz interakcje między różnymi współbieżnymi częściami aplikacji?

To zależy od kontekstu. W przypadku scenariuszy pracowniczych myślę o podwładnym kierownika. Menedżer mówi podwładnemu, aby coś zrobił i czeka na aktualizacje statusu.

W przypadku jednoczesnych, niepowiązanych zadań myślę o windach lub samochodach na osobnych pasach ruchu.

Do synchronizacji czasami myślę o światłach lub stylach zakrętów.

Również jeśli używasz C # 4.0, możesz rzucić okiem na bibliotekę zadań równoległych

Conrad Frix
źródło
0

Moja odpowiedź na te pytania to:

  • Jak wymyślić, co można uczynić współbieżnym z tym, co musi być sekwencyjne?

Najpierw muszę wiedzieć, dlaczego powinienem używać współbieżności, ponieważ dowiedziałem się, że ludzie ekscytują się ideą współbieżności, ale nie zawsze myślą o problemie, który próbują rozwiązać.

Jeśli musisz zasymulować rzeczywistą sytuację, taką jak kolejki, przepływy pracy itp., Najprawdopodobniej będziesz musiał zastosować podejście równoczesne.

Teraz, gdy wiem, że powinienem go użyć, nadszedł czas, aby przeanalizować kompromis, jeśli masz dużo procesów, możesz pomyśleć o narzutach komunikacyjnych, ale jeśli musisz nowe, może się nie skończyć z równoczesnym rozwiązaniem (reanalizuj problem, jeśli więc.)

  • Jak odtworzyć warunki błędu i zobaczyć, co dzieje się podczas działania aplikacji?

Nie jestem ekspertem w tej kwestii, ale myślę, że w przypadku systemów współbieżnych nie jest to właściwe podejście. Należy wybrać podejście teoretyczne, szukając 4 wymogów impasu w obszarach krytycznych:

  1. Brak prewencyjności
  2. Trzymaj i czekaj
  3. Wykluczenie motoryczne
  4. Okrągły łańcuch

    • Jak wizualizujesz interakcje między różnymi współbieżnymi częściami aplikacji?

Staram się najpierw ustalić, kto jest uczestnikiem interakcji, a potem, jak się komunikuje i z kim. Wreszcie wykresy i diagramy interakcji pomagają mi wizualizować. Mojej dobrej starej tablicy nie da się pobić żadnym innym rodzajem mediów.

guiman
źródło
0

Będę tępy. Uwielbiam narzędzia. Używam wielu narzędzi. Moim pierwszym krokiem jest wytyczenie zamierzonych ścieżek przepływu stanu. Kolejnym krokiem jest próba ustalenia, czy warto, czy też wymagany przepływ informacji zbyt często powoduje, że kod jest szeregowy. Następnie spróbuję zaprojektować kilka prostych modeli. Mogą to być stosy surowych rzeźb wykałaczek do prostych podobnych przykładów w pythonie. Następnie przeglądam kilka moich ulubionych książek, takich jak mała księga semaforów, i sprawdzam, czy ktoś już nie znalazł lepszego rozwiązania mojego problemu.

Potem zaczynam kodować.
Żartuję. Najpierw trochę więcej badań. Lubię usiąść z innym hakerem i przejść przez oczekiwane wykonanie programu na wysokim poziomie. Jeśli pojawią się pytania, przechodzimy na niższy poziom. Ważne jest, aby dowiedzieć się, czy ktoś inny może zrozumieć twoje rozwiązanie wystarczająco dobrze, aby je utrzymać.

Wreszcie zaczynam kodować. Najpierw staram się, aby było to bardzo proste. Tylko ścieżka kodu, nic szczególnego. Przenieś jak najmniejszy stan. Unikaj pisania. Unikaj odczytów, które mogą kolidować z zapisami. Unikaj przede wszystkim pisania, które może kolidować z zapisami. Bardzo łatwo jest stwierdzić, że masz ich pozytywnie toksyczną liczbę, a twoje piękne rozwiązanie nagle staje się czymś więcej niż tylko seryjnym podejściem.

Dobrą zasadą jest używanie ram tam, gdzie to możliwe. Jeśli sam piszesz podstawowe komponenty wątków, takie jak dobre zsynchronizowane struktury danych, lub Boże, prawdziwe prymitywy synchronizacji, prawie na pewno zdmuchniesz całą nogę.

Wreszcie narzędzia. Debugowanie jest bardzo trudne. Używam valgrind \ callgrind na Linuksie w połączeniu z PINem i równoległe studia na Windowsie. Nie próbuj debugować tych rzeczy ręcznie. Prawdopodobnie możesz. Ale prawdopodobnie będziesz żałować, że tego nie zrobiłeś. Dziesięć godzin opanowanie potężnych narzędzi i dobrych modeli pozwoli Ci zaoszczędzić setki godzin później.

Przede wszystkim pracuj stopniowo. Pracuj ostrożnie. Nie pisz współbieżnego kodu, gdy jesteś zmęczony. Nie pisz tego, gdy jesteś głodny. W rzeczywistości, jeśli możesz tego uniknąć, po prostu nie pisz tego. Współbieżność jest trudna i zauważyłem, że wiele aplikacji, które wymieniają ją jako funkcję, często są dostarczane z nią jako jedyną funkcją.

Podsumowując:
Begin:
Think
Talk
Test
Napisz po prostu
Przeczytaj
test Test
Napisz
Debuguj
GOTO Rozpocznij

Jake Kurzer
źródło