Do tej pory unikałem koszmaru, który testuje wielowątkowy kod, ponieważ wydaje się, że to zbyt duże pole minowe. Chciałbym zapytać, w jaki sposób ludzie testowali kod, który opiera się na wątkach w celu pomyślnego wykonania, lub po prostu, jak ludzie testowali tego rodzaju problemy, które pojawiają się tylko wtedy, gdy dwa wątki oddziałują w określony sposób?
Wydaje się, że jest to dziś naprawdę kluczowy problem dla programistów, przydatne byłoby połączenie naszej wiedzy na temat tego jednego imho.
Odpowiedzi:
Słuchaj, nie ma łatwego sposobu na zrobienie tego. Pracuję nad projektem z natury wielowątkowym. Zdarzenia przychodzą z systemu operacyjnego i muszę je przetwarzać jednocześnie.
Najprostszym sposobem radzenia sobie z testowaniem złożonego, wielowątkowego kodu aplikacji jest: Jeśli jest zbyt skomplikowany do przetestowania, robisz to źle. Jeśli masz jedną instancję, na którą działa wiele wątków, i nie możesz przetestować sytuacji, w których te wątki przebiegają jeden po drugim, projekt musi zostać przerobiony. Jest to tak proste, jak i tak złożone.
Istnieje wiele sposobów programowania wielowątkowości, które pozwalają uniknąć jednoczesnego uruchamiania wątków przez instancje. Najprostszym jest uczynienie wszystkich twoich obiektów niezmiennymi. Oczywiście nie jest to zazwyczaj możliwe. Musisz więc zidentyfikować miejsca w projekcie, w których wątki wchodzą w interakcje z tym samym wystąpieniem i zmniejszyć liczbę tych miejsc. W ten sposób izolujesz kilka klas, w których faktycznie występuje wielowątkowość, zmniejszając ogólną złożoność testowania systemu.
Ale musisz zdać sobie sprawę, że nawet robiąc to, nadal nie możesz przetestować każdej sytuacji, w której dwa wątki na siebie natrafiają. Aby to zrobić, musisz uruchomić dwa wątki jednocześnie w tym samym teście, a następnie dokładnie kontrolować, które wiersze wykonują w danym momencie. Najlepsze, co możesz zrobić, to zasymulować tę sytuację. Ale może to wymagać kodowania specjalnie do testowania, a to w najlepszym razie pół kroku w kierunku prawdziwego rozwiązania.
Prawdopodobnie najlepszym sposobem przetestowania kodu pod kątem problemów z wątkami jest statyczna analiza kodu. Jeśli Twój wątkowy kod nie jest zgodny ze skończonym zestawem bezpiecznych wątków, możesz mieć problem. Wierzę, że analiza kodu w VS zawiera pewną wiedzę na temat wątków, ale prawdopodobnie niewiele.
Spójrz, ponieważ sytuacja jest obecnie (i prawdopodobnie będzie to dobry moment, aby przyjść), najlepszym sposobem na przetestowanie aplikacji wielowątkowych jest zmniejszenie złożoności kodu wątkowego w jak największym stopniu. Minimalizuj obszary interakcji wątków, testuj jak najlepiej i wykorzystuj analizę kodu, aby identyfikować niebezpieczne obszary.
źródło
Minęło trochę czasu, kiedy to pytanie zostało opublikowane, ale wciąż nie ma odpowiedzi ...
odpowiedź kleolb02 jest dobra. Spróbuję wejść w więcej szczegółów.
Jest sposób, który ćwiczę dla kodu C #. W przypadku testów jednostkowych powinieneś być w stanie zaprogramować powtarzalne testy, co jest największym wyzwaniem w kodzie wielowątkowym. Tak więc moja odpowiedź ma na celu wymuszenie kodu asynchronicznego na testowej uprzęży, która działa synchronicznie .
Jest to pomysł z książki Gerarda Meszardosa „ Wzorce testowe xUnit ” i nazywa się „Humble Object” (s. 695): Musisz oddzielić podstawowy kod logiczny i wszystko, co pachnie od siebie kodem asynchronicznym. Wynikiem tego byłaby klasa dla podstawowej logiki, która działa synchronicznie .
To pozwala ci na przetestowanie rdzenia logicznego w sposób synchroniczny . Masz absolutną kontrolę nad czasem wykonywania połączeń w oparciu o logikę podstawową, dzięki czemu możesz wykonywać powtarzalne testy. I to jest Twoja korzyść z oddzielenia logiki rdzenia i logiki asynchronicznej.
Ta podstawowa logika musi być otoczona przez inną klasę, która jest odpowiedzialna za asynchroniczne odbieranie wywołań logiki podstawowej i deleguje te wywołania do logiki podstawowej. Kod produkcyjny będzie miał dostęp do podstawowej logiki tylko za pośrednictwem tej klasy. Ponieważ ta klasa powinna jedynie delegować połączenia, jest to bardzo „głupia” klasa bez dużej logiki. Możesz więc utrzymać swoje testy jednostkowe dla tej asychronicznej klasy robotniczej na minimum.
Wszystko powyżej (testowanie interakcji między klasami) to testy składowe. Również w tym przypadku powinieneś mieć całkowitą kontrolę nad czasem, jeśli będziesz trzymać się wzorca „Humble Object”.
źródło
Naprawdę trudny! W moich testach jednostkowych (C ++) podzieliłem to na kilka kategorii zgodnie z zastosowanym wzorem współbieżności:
Testy jednostkowe dla klas, które działają w jednym wątku i nie są świadome wątku - łatwe, przetestuj jak zwykle.
Testy jednostkowe obiektów Monitor (wykonujących zsynchronizowane metody w wątku kontrolnym wywołującego), które ujawniają zsynchronizowany publiczny interfejs API - tworzą wiele próbnych wątków, które wykonują interfejs API. Twórz scenariusze wykorzystujące wewnętrzne warunki obiektu pasywnego. Dołącz jeden dłuższy test, który w zasadzie bije go od wielu wątków przez długi czas. Wiem, że to nienaukowe, ale buduje zaufanie.
Testy jednostkowe dla obiektów aktywnych (tych, które otaczają własny wątek lub wątki kontroli) - podobnie jak w punkcie 2 powyżej, z odmianami zależnymi od projektu klasy. Publiczny interfejs API może blokować lub nie blokować, osoby dzwoniące mogą uzyskać kontrakty terminowe, dane mogą przybywać do kolejek lub wymagać kolejkowania. Możliwych jest tutaj wiele kombinacji; białe pudełko z dala. Nadal wymaga wielu próbnych wątków, aby wywoływać testowany obiekt.
Tak na marginesie:
Podczas wewnętrznych szkoleń dla programistów uczę Pillars of Concurrency i tych dwóch wzorców jako podstawowej struktury myślenia i rozwiązywania problemów związanych z współbieżnością. Istnieją oczywiście bardziej zaawansowane koncepcje, ale odkryłem, że ten zestaw podstaw pomaga inżynierom unikać zupy. Prowadzi to również do kodu, który jest bardziej testowalny jednostkowo, jak opisano powyżej.
źródło
W ostatnich latach napotkałem ten problem kilka razy, pisząc kod obsługi wątków dla kilku projektów. Podaję spóźnioną odpowiedź, ponieważ większość innych odpowiedzi, podając alternatywy, w rzeczywistości nie odpowiadają na pytanie dotyczące testowania. Moja odpowiedź jest skierowana do przypadków, w których nie ma alternatywy dla kodu wielowątkowego; Zajmuję się projektowaniem kodu pod kątem kompletności, ale także omawiam testy jednostkowe.
Pisanie testowanego wielowątkowego kodu
Pierwszą rzeczą do zrobienia jest oddzielenie kodu obsługi wątku produkcyjnego od całego kodu, który faktycznie przetwarza dane. W ten sposób przetwarzanie danych można przetestować jako kod pojedynczo wątkowy, a jedyną rzeczą, którą robi kod wielowątkowy, jest koordynacja wątków.
Drugą rzeczą do zapamiętania jest to, że błędy w kodzie wielowątkowym są probabilistyczne; błędy, które objawiają się najrzadziej, to błędy, które przekradną się do produkcji, będą trudne do odtworzenia nawet podczas produkcji, a zatem spowodują największe problemy. Z tego powodu standardowe podejście do kodowania polegające na szybkim pisaniu kodu, a następnie debugowaniu go, aż zadziała, jest złym pomysłem na kod wielowątkowy; spowoduje to kod, w którym łatwe błędy zostaną naprawione, a niebezpieczne błędy będą nadal występować.
Zamiast tego, pisząc kod wielowątkowy, musisz napisać kod z takim nastawieniem, że unikniesz pisania błędów. Jeśli poprawnie usunąłeś kod przetwarzania danych, kod obsługi wątku powinien być wystarczająco mały - najlepiej kilka wierszy, w najgorszym przypadku kilkadziesiąt wierszy - abyś miał szansę napisać go bez pisania błędu, a na pewno bez pisania wielu błędów , jeśli rozumiesz wątki, nie spiesz się i zachowaj ostrożność.
Pisanie testów jednostkowych dla kodu wielowątkowego
Po napisaniu kodu wielowątkowego tak ostrożnie, jak to możliwe, nadal warto pisać testy dla tego kodu. Głównym celem testów jest nie tyle testowanie błędów związanych z wyścigiem w zależności od czasu - niemożliwe jest powtarzalne testowanie takich warunków wyścigu - ale raczej sprawdzenie, czy twoja strategia blokowania zapobiegająca takim błędom pozwala na interakcję wielu wątków zgodnie z przeznaczeniem .
Aby poprawnie przetestować prawidłowe działanie blokujące, test musi rozpocząć wiele wątków. Aby test był powtarzalny, chcemy, aby interakcje między wątkami zachodziły w przewidywalnej kolejności. Nie chcemy zewnętrznie synchronizować wątków w teście, ponieważ to maskuje błędy, które mogą się zdarzyć podczas produkcji, w których wątki nie są synchronizowane zewnętrznie. Pozostawia to użycie opóźnień czasowych do synchronizacji wątków, co jest techniką, którą z powodzeniem stosowałem za każdym razem, gdy musiałem pisać testy wielowątkowego kodu.
Jeśli opóźnienia są zbyt krótkie, test staje się kruchy, ponieważ niewielkie różnice czasowe - powiedzmy między różnymi maszynami, na których testy mogą być uruchomione - mogą spowodować, że czas się wyłączy i test się nie powiedzie. To, co zwykle robiłem, to zaczynać od opóźnień, które powodują niepowodzenia testów, zwiększać opóźnienia, aby test przebiegał niezawodnie na mojej maszynie programistycznej, a następnie podwajać opóźnienia poza tym, aby test miał duże szanse na przekazanie na inne maszyny. Oznacza to, że test zajmie makroskopowo dużo czasu, choć z mojego doświadczenia wynika, że staranne zaprojektowanie testu może ograniczyć ten czas do nie więcej niż kilkunastu sekund. Ponieważ nie powinieneś mieć zbyt wielu miejsc wymagających kodu koordynacji wątków w swojej aplikacji, powinno to być dopuszczalne dla twojego zestawu testów.
Na koniec śledź liczbę błędów wykrytych w teście. Jeśli Twój test obejmuje 80% pokrycia kodu, można oczekiwać, że wykryje około 80% twoich błędów. Jeśli Twój test jest dobrze zaprojektowany, ale nie wykrył żadnych błędów, istnieje uzasadniona szansa, że nie masz dodatkowych błędów, które pojawią się tylko podczas produkcji. Jeśli test wykryje jeden lub dwa błędy, nadal możesz mieć szczęście. Poza tym, możesz rozważyć dokładną analizę lub nawet całkowite przepisanie kodu obsługi wątków, ponieważ jest prawdopodobne, że kod nadal zawiera ukryte błędy, które będą bardzo trudne do znalezienia, dopóki kod nie będzie produkowany, i bardzo trudne do naprawienia.
źródło
Miałem też poważne problemy z testowaniem wielowątkowego kodu. Potem znalazłem naprawdę fajne rozwiązanie w „Wzorcach testowych xUnit” Gerarda Meszarosa. Wzór, który opisuje, nazywa się Humble object .
Zasadniczo opisuje, jak można wyodrębnić logikę do oddzielnego, łatwego do przetestowania komponentu, który jest oddzielony od swojego środowiska. Po przetestowaniu tej logiki można przetestować skomplikowane zachowanie (wielowątkowość, wykonywanie asynchroniczne itp.)
źródło
Istnieje kilka narzędzi, które są całkiem dobre. Oto podsumowanie niektórych z Java.
Niektóre dobre narzędzia do analizy statycznej to FindBugs (zawiera przydatne wskazówki), JLint , Java Pathfinder (JPF i JPF2) i Bogor .
MultithreadedTC to całkiem dobre narzędzie do analizy dynamicznej (zintegrowane z JUnit), w którym musisz skonfigurować własne przypadki testowe.
ConTest z IBM Research jest interesujący. Instrumentuje Twój kod, wstawiając wszelkiego rodzaju zachowania modyfikujące wątek (np. Uśpienie i wydajność), aby spróbować losowo wykryć błędy.
SPIN to naprawdę fajne narzędzie do modelowania komponentów Java (i innych), ale musisz mieć jakiś użyteczny framework. Jest trudny w użyciu, ale jest niezwykle wydajny, jeśli wiesz, jak go używać. Sporo narzędzi używa SPIN pod maską.
MultithreadedTC jest prawdopodobnie najbardziej popularnym nurtem, ale niektóre z wymienionych powyżej narzędzi analizy statycznej są zdecydowanie warte spojrzenia.
źródło
Oczekiwanie może być również pomocne w pisaniu deterministycznych testów jednostkowych. Pozwala poczekać, aż jakiś stan w twoim systemie zostanie zaktualizowany. Na przykład:
lub
Obsługuje również Scala i Groovy.
źródło
Innym sposobem (trochę) testowania kodu wątkowego i ogólnie bardzo złożonych systemów jest testowanie Fuzz . To nie jest świetne i nie znajdzie wszystkiego, ale prawdopodobnie będzie przydatne i łatwe do zrobienia.
Zacytować:
źródło
Zrobiłem dużo tego i tak, to do bani.
Kilka porad:
throwable
pole i zaznacz jetearDown
(patrz Listing 1). Jeśli złapiesz zły wyjątek w innym wątku, po prostu przypisz go do rzucania.AtomicBoolean
w swoich testach. Jest bezpieczny dla wątków i często potrzebujesz końcowego typu odwołania do przechowywania wartości z klas wywołań zwrotnych i tym podobnych. Zobacz przykład z listingu 3.@Test(timeout=60*1000)
), Ponieważ testy współbieżności mogą czasami zawieść na zawsze, gdy są zepsute.Listing 1:
Listing 2:
Listing 3:
źródło
Testowanie poprawności kodu MT jest, jak już wspomniano, dość trudnym problemem. Ostatecznie sprowadza się to do zapewnienia, że w twoim kodzie nie ma niepoprawnie zsynchronizowanych wyścigów danych. Problem polega na tym, że istnieje nieskończenie wiele możliwości wykonywania wątków (przeplatania), nad którymi nie masz dużej kontroli (koniecznie przeczytaj ten artykuł). W prostych scenariuszach można faktycznie udowodnić poprawność za pomocą rozumowania, ale zazwyczaj tak nie jest. Zwłaszcza jeśli chcesz uniknąć / zminimalizować synchronizację i nie wybierać najbardziej oczywistej / najłatwiejszej opcji synchronizacji.
Podejście, które stosuję, polega na pisaniu wysoce współbieżnego kodu testowego, aby potencjalnie niewykryte wyścigi danych mogły wystąpić. A potem przez jakiś czas przeprowadzam te testy :) Pewnego razu natknąłem się na rozmowę, w której jakiś informatyk pokazał narzędzie, które to robi (losowo opracowuje test ze specyfikacji, a następnie uruchamia je w sposób szalony, równolegle, sprawdzając określone niezmienniki być zepsutym).
Nawiasem mówiąc, myślę, że ten aspekt testowania kodu MT nie został tutaj wymieniony: zidentyfikuj niezmienniki kodu, które możesz sprawdzić losowo. Niestety znalezienie tych niezmienników jest również dość trudnym problemem. Ponadto mogą nie utrzymywać się przez cały czas podczas wykonywania, więc musisz znaleźć / egzekwować punkty wykonania, w których można oczekiwać, że będą prawdziwe. Doprowadzenie wykonania kodu do takiego stanu jest również trudnym problemem (i może samo w sobie powodować problemy z współbieżnością. Cholera, to cholernie trudne!
Kilka interesujących linków do przeczytania:
źródło
Pete Goodliffe ma serię testów jednostkowych kodu wątkowego .
To trudne. Wybieram łatwiejsze wyjście i staram się nie wyodrębniać kodu wątków z rzeczywistego testu. Pete wspomina, że sposób, w jaki to robię, jest zły, ale albo dobrze się rozdzieliłem, albo po prostu miałem szczęście.
źródło
Jeśli chodzi o Javę, sprawdź rozdział 12 JCIP . Istnieje kilka konkretnych przykładów pisania deterministycznych, wielowątkowych testów jednostkowych, aby przynajmniej przetestować poprawność i niezmienniki współbieżnego kodu.
„Sprawdzanie” bezpieczeństwa wątków za pomocą testów jednostkowych jest znacznie trudniejsze. Wierzę, że lepiej temu służą automatyczne testy integracji na różnych platformach / konfiguracjach.
źródło
Lubię pisać dwie lub więcej metod testowych do wykonania na równoległych wątkach i każda z nich wywołuje testowany obiekt. Używam wywołań Sleep () do koordynowania kolejności wywołań z różnych wątków, ale to nie jest naprawdę niezawodne. Jest również o wiele wolniejszy, ponieważ musisz spać wystarczająco długo, aby czas działał.
Znalazłem bibliotekę wielowątkową TC Java z tej samej grupy, która napisała FindBugs. Pozwala określić kolejność zdarzeń bez użycia Sleep () i jest niezawodny. Jeszcze tego nie próbowałem.
Największym ograniczeniem tego podejścia jest to, że pozwala jedynie przetestować scenariusze, które, jak podejrzewasz, spowodują problemy. Jak powiedzieli inni, naprawdę musisz odizolować swój wielowątkowy kod na niewielką liczbę prostych klas, aby mieć nadzieję na ich dokładne przetestowanie.
Po dokładnym przetestowaniu scenariuszy, które prawdopodobnie spowodują problemy, nienaukowy test, który przez pewien czas wysyła do klasy kilka równoczesnych próśb, jest dobrym sposobem na znalezienie nieoczekiwanych problemów.
Aktualizacja: Grałem trochę z biblioteką Multithreaded TC Java i działa dobrze. Przeniesiłem również niektóre jego funkcje do wersji .NET, którą nazywam TickingTest .
źródło
Testy jednostkowe elementów gwintowych wykonuję w taki sam sposób, jak w przypadku każdego testu jednostkowego, tj. Z odwróceniem ram kontroli i izolacji. Rozwijam się na arenie .Net i po wyjęciu z pudełka wątki (między innymi) są bardzo trudne (powiedziałbym, że prawie niemożliwe) do pełnej izolacji.
Dlatego napisałem opakowania, które wyglądają mniej więcej tak (uproszczone):
Stamtąd mogę łatwo wstrzyknąć program IThreadingManager do moich komponentów i użyć wybranej struktury izolacji, aby wątek zachowywał się zgodnie z oczekiwaniami podczas testu.
To do tej pory działało świetnie dla mnie i używam tego samego podejścia do puli wątków, rzeczy w System.Environment, Sleep itp.
źródło
Zobacz moją pokrewną odpowiedź na
Projektowanie klasy testowej dla niestandardowej bariery
Jest nastawiony na Javę, ale ma rozsądne podsumowanie opcji.
Podsumowując (IMO), to nie jest użycie jakiegoś fantazyjnego frameworka, który zapewni poprawność, ale to, jak zabierzesz się do projektowania wielowątkowego kodu. Podział obaw (współbieżność i funkcjonalność) to ogromny sposób na zwiększenie zaufania. Rosnące oprogramowanie obiektowe oparte na testach wyjaśnia niektóre opcje lepiej niż ja.
Analiza statyczna i metody formalne (patrz Współbieżność: modele stanowe i programy Java ) są opcją, ale znalazłem, że mają one ograniczone zastosowanie w rozwoju komercyjnym.
Nie zapominaj, że wszelkie testy stylu ładowania / namaczania rzadko gwarantują zwrócenie uwagi na problemy.
Powodzenia!
źródło
tempus-fugit
bibliotece, którahelps write and test concurrent code
;)Niedawno odkryłem (dla Javy) narzędzie o nazwie Threadsafe. Jest to narzędzie do analizy statycznej podobne do findbugs, ale specjalnie do wykrywania problemów z wielowątkowością. Nie zastępuje on testowania, ale mogę go polecić w ramach pisania niezawodnej, wielowątkowej Java.
Łapie nawet niektóre bardzo subtelne potencjalne problemy związane z takimi kwestiami, jak subsumpcja klas, uzyskiwanie dostępu do niebezpiecznych obiektów za pośrednictwem współbieżnych klas i wykrywanie brakujących lotnych modyfikatorów podczas korzystania z podwójnie sprawdzonego paradygmatu blokowania.
Jeśli piszesz wielowątkową Javę, spróbuj .
źródło
Poniższy artykuł sugeruje 2 rozwiązania. Zawijanie semafora (CountDownLatch) i dodaje takie funkcje, jak eksternalizacja danych z wewnętrznego wątku. Innym sposobem osiągnięcia tego celu jest użycie puli wątków (patrz Użyteczne miejsca).
Zraszacz - zaawansowany obiekt synchronizacji
źródło
Większość ostatniego tygodnia spędziłem w bibliotece uniwersyteckiej, studiując debugowanie współbieżnego kodu. Głównym problemem jest to, że współbieżny kod nie jest deterministyczny. Zazwyczaj debugowanie akademickie przypada tutaj na jeden z trzech obozów:
Teraz, jak zauważyli wyżej komentatorzy, możesz zaprojektować swój system współbieżny w bardziej deterministyczny sposób. Jeśli jednak nie zrobisz tego poprawnie, wrócisz do projektowania systemu sekwencyjnego.
Moją sugestią byłoby skoncentrowanie się na bardzo ścisłym protokole projektowym dotyczącym tego, co jest wątkowane, a co nie. Jeśli ograniczysz interfejs tak, aby między elementami były minimalne zależności, jest to o wiele łatwiejsze.
Powodzenia i pracuj nad tym problemem.
źródło
Miałem niefortunne zadanie testowania kodu wątkowego i są to zdecydowanie najtrudniejsze testy, jakie kiedykolwiek napisałem.
Pisząc testy, użyłem kombinacji delegatów i wydarzeń. Zasadniczo chodzi o używanie
PropertyNotifyChanged
zdarzeń z jednymWaitCallback
lub kilkoma tego typuConditionalWaiter
ankietami.Nie jestem pewien, czy to było najlepsze podejście, ale mi się udało.
źródło
Zakładanie kodu „wielowątkowego” oznaczało coś, co jest
Innymi słowy, mówimy o testowaniu niestandardowej, stanowej, bezpiecznej dla wątków klasy / metody / jednostki - która powinna być obecnie bardzo rzadką bestią.
Ponieważ ta bestia jest rzadka, przede wszystkim musimy upewnić się, że istnieją wszystkie uzasadnione wymówki, aby ją napisać.
Krok 1. Rozważ zmianę stanu w tym samym kontekście synchronizacji.
Dzisiaj łatwo jest napisać współbieżny i asynchroniczny kod, w którym operacje we / wy lub inne powolne operacje są odciążone w tle, ale stan współużytkowany jest aktualizowany i sprawdzany w jednym kontekście synchronizacji. np. asynchronizuj / czekaj na zadania i Rx w .NET itp. - wszystkie są testowalne z założenia, „rzeczywiste” zadania i harmonogramy można zastąpić testowaniem deterministycznym (jednak nie wchodzi to w zakres pytania).
To może wydawać się bardzo ograniczone, ale to podejście działa zaskakująco dobrze. Możliwe jest pisanie całych aplikacji w tym stylu bez konieczności zabezpieczania wątków przed jakimkolwiek stanem (tak robię).
Krok 2. Jeśli manipulowanie stanem współdzielonym w kontekście pojedynczej synchronizacji jest absolutnie niemożliwe.
Upewnij się, że koło nie zostało wynalezione na nowo / zdecydowanie nie ma standardowej alternatywy, którą można by dostosować do pracy. Powinno być prawdopodobne, że kod jest bardzo spójny i zawiera się w jednej jednostce, np. Z dużym prawdopodobieństwem jest to specjalny przypadek pewnej standardowej, bezpiecznej dla wątków struktury danych, takiej jak mapa hash lub kolekcja.
Uwaga: jeśli kod jest duży / obejmuje wiele klas ORAZ wymaga manipulacji stanem wielowątkowym, istnieje bardzo duża szansa, że projekt nie jest dobry, ponownie rozważ krok 1
Krok 3. Jeśli ten krok zostanie osiągnięty, musimy przetestować naszą własną, stanową, bezpieczną dla wątków klasę / metodę / jednostkę .
Będę śmiertelnie szczery: nigdy nie musiałem pisać odpowiednich testów dla takiego kodu. Przez większość czasu uciekam w kroku 1, czasem w kroku 2. Ostatnim razem, gdy musiałem napisać niestandardowy kod bezpieczny dla wątków, było tak wiele lat temu, że to było przed przyjęciem testowania jednostkowego / prawdopodobnie nie musiałbym go pisać i tak z obecną wiedzą.
Gdybym naprawdę musiał przetestować taki kod ( wreszcie rzeczywista odpowiedź ), spróbowałbym kilku rzeczy poniżej
Niedeterministyczne testy warunków skrajnych. np. uruchom 100 wątków jednocześnie i sprawdź, czy wynik końcowy jest spójny. Jest to bardziej typowe w przypadku testów na wyższym poziomie / w testach integracyjnych wielu użytkowników, ale można je również stosować na poziomie jednostki.
Odsłoń niektóre „zaczepy” testowe, w których test może wstrzyknąć kod, aby pomóc w tworzeniu deterministycznych scenariuszy, w których jeden wątek musi wykonać operację przed drugim. Choć jest tak brzydka, nie mogę wymyślić nic lepszego.
Testy oparte na opóźnieniu w celu uruchomienia wątków i wykonywania operacji w określonej kolejności. Ściśle mówiąc, takie testy również nie są deterministyczne (istnieje możliwość zatrzymania / zatrzymania systemu GC w kolekcji, co może zniekształcić opóźnienie w innym przypadku), jest również brzydkie, ale pozwala uniknąć haków.
źródło
Do kodu J2E użyłem SilkPerformer, LoadRunner i JMeter do testowania współbieżności wątków. Wszyscy robią to samo. Zasadniczo zapewniają one stosunkowo prosty interfejs do administrowania ich wersją serwera proxy, niezbędny do analizy strumienia danych TCP / IP i symulacji wielu użytkowników wysyłających jednocześnie żądania do serwera aplikacji. Serwer proxy może dać ci możliwość robienia takich rzeczy, jak analizowanie złożonych żądań, prezentując całą stronę i adres URL wysłany do serwera, a także odpowiedź z serwera, po przetworzeniu żądania.
Niektóre błędy można znaleźć w niepewnym trybie http, w którym można przynajmniej analizować przesyłane dane formularzy i systematycznie je zmieniać dla każdego użytkownika. Ale prawdziwe testy są przeprowadzane w trybie https (Secured Socket Layers). Następnie musisz również zmagać się z systematyczną zmianą danych sesji i plików cookie, które mogą być nieco bardziej skomplikowane.
Najlepszy błąd, jaki kiedykolwiek znalazłem, podczas testowania współbieżności, polegał na tym, że odkryłem, że programista polegał na zbieraniu pamięci Java w celu zamknięcia żądania połączenia ustanowionego podczas logowania do serwera LDAP podczas logowania. Spowodowało to ujawnienie użytkowników do sesji innych użytkowników i bardzo mylących wyników, gdy próbujesz przeanalizować, co się stało, gdy serwer upadł na kolana, ledwo co sfinalizując jedną transakcję, co kilka sekund.
W końcu ty lub ktoś prawdopodobnie będziesz musiał zapiąć pasy i przeanalizować kod pod kątem błędów, takich jak ten, o którym właśnie wspomniałem. Najbardziej użyteczna jest otwarta dyskusja między działami, taka jak ta, która miała miejsce, kiedy rozwinęliśmy opisany powyżej problem. Ale te narzędzia są najlepszym rozwiązaniem do testowania wielowątkowego kodu. JMeter jest oprogramowaniem typu open source. SilkPerformer i LoadRunner są zastrzeżone. Jeśli naprawdę chcesz wiedzieć, czy Twoja aplikacja jest bezpieczna dla wątków, tak robią to duzi chłopcy. Zrobiłem to dla bardzo dużych firm profesjonalnie, więc nie zgaduję. Mówię z własnego doświadczenia.
Słowo ostrzeżenia: zrozumienie tych narzędzi zajmuje trochę czasu. Nie będzie to po prostu instalacja oprogramowania i uruchamianie GUI, chyba że masz już styczność z programowaniem wielowątkowym. Próbowałem zidentyfikować 3 kluczowe kategorie obszarów do zrozumienia (formularze, dane sesji i pliki cookie), mając nadzieję, że przynajmniej rozpoczęcie od zrozumienia tych tematów pomoże Ci skoncentrować się na szybkich wynikach, zamiast konieczności zapoznania się z cała dokumentacja.
źródło
Współbieżność to złożona gra między modelem pamięci, sprzętem, pamięciami podręcznymi i naszym kodem. W przypadku Javy przynajmniej takie testy zostały częściowo rozwiązane głównie przez jcstress . Twórcy tej biblioteki są znani jako autorzy wielu funkcji współbieżności JVM, GC i Java.
Ale nawet ta biblioteka wymaga dobrej znajomości specyfikacji Java Memory Model, abyśmy dokładnie wiedzieli, co testujemy. Myślę jednak, że celem tego wysiłku są znaki mircobenchmark. Niezbyt duże aplikacje biznesowe.
źródło
Jest artykuł na ten temat, używając Rust jako języka w przykładowym kodzie:
https://medium.com/@polyglot_factotum/rust-concurrency-five-easy-pieces-871f1c62906a
Podsumowując, sztuczka polega na napisaniu logiki współbieżnej tak, aby była odporna na niedeterminizm związany z wieloma wątkami wykonania, przy użyciu narzędzi takich jak kanały i zmienne.
Następnie, jeśli tak ustrukturyzowałeś swoje „komponenty”, najłatwiejszym sposobem przetestowania ich jest użycie kanałów do wysłania do nich wiadomości, a następnie zablokowanie innych kanałów w celu potwierdzenia, że komponent wysyła określone oczekiwane wiadomości.
Link do artykułu jest w pełni napisany przy użyciu testów jednostkowych.
źródło
Jeśli testujesz prosty nowy wątek (runnable) .run () Możesz wyśmiewać Thread, aby sekwencyjnie uruchamiać runnable
Na przykład, jeśli kod testowanego obiektu wywołuje taki nowy wątek
Następnie pomocne może być wykpiwanie nowych wątków i sekwencyjne uruchamianie argumentu uruchamialnego
źródło
(jeśli to możliwe) nie używaj wątków, używaj aktorów / aktywnych obiektów. Łatwy do przetestowania.
źródło
Możesz użyć EasyMock.makeThreadSafe, aby instancja testowa była bezpieczna dla wątków
źródło