2011-09-16 12 views
6

Muszę napisać test jednostkowy, który wywołuje stan wyścigu, aby przetestować, czy prawdopodobnie rozwiązałem problem później. Problem polega na tym, że stan wyścigu występuje bardzo rzadko, może dlatego, że mój komputer ma tylko dwa rdzenie.Wywołanie warunków wyścigu w Javie

Kod jest coś takiego, co następuje:

class MyDateTime { 
    String getColonTime() { 
    // datetime is some kind of lazy caching variable declared somewhere(does not matter) 
    if (datetime == null) { 
     initDateTime(); //Uses lazy to initlialize variable, takes some time 
    } 
    // Colon time stores hh:mm as string 
    if (datetime.colonTime == null) { 
     StringBuilder sb = new StringBuilder(); 
     //Now do some steps to build the hh:mm string 
     //... 
     //set colon time 
     datetime.colonTime = sb.toString(); 
    } 
    return datetime.colonTime; 
    } 
} 

Objaśnienie: initDateTime przypisuje nową instancję do DateTime nich, datetime.colonTime jest null potem (jak chcemy zainicjować to leniwe, jak stwierdziłem przed). Teraz, jeśli wątek A wprowadza metodę, a następnie program planujący zatrzymuje ją tuż przed uruchomieniem metody initDateTime(). Wątek B teraz runst getColonTime(), widzi, że data/godzina jest nadal pusta i inicjuje ją. datetime.colonTime ma wartość null, więc drugi blok if jest wykonywany, a datetime.colonTime pobiera wartość StringBuilder. Jeśli następnie program planujący zatrzyma wątek między tą linią a instrukcją return i wznowi wątek A, następuje: Ponieważ A zostało zatrzymane tuż przed wywołaniem initDateTime, A wywołuje teraz initDateTime(), która będzie w pewnym sensie zresetować obiekt datetime, ustawiając datetime.colonTime ponownie na wartość null. Wątek A następnie wchodzi do drugiego bloku if, ale program planujący przerwie A przed datetime.colonTime = sb.toString(); jest nazywany. Podsumowując, dateTime.colonTime jest wciąż zerowy. Teraz program planujący wznawia B, a metoda zwraca wartość null.

Próbowałem sprowokować warunki wyścigu, mając wiele wątków wywołujących getColonTime() do pojedynczej (końcowej) instancji MyDateTime, ale zawodzi tylko w niektórych wyjątkowo rzadkich przypadkach :( Wszelkie wskazówki, jak napisać JUnit "test"?

+3

Możesz spróbować najpierw użyć debuggera, aby sprowokować stan wyścigu. To znaczy. uruchom wątek, złap go w punkcie przerwania (np. między jeśli jest) i uruchom inny - i tak dalej. Po uzyskaniu idei, w jaki sposób RC się dzieje (wydaje się, że nie masz tego teraz) możesz napisać udany test jednostkowy – pupssman

+0

Nie widzę, jak można dostać się do 'daty powrotu.colonTime; 'i odzyskaj null. Czy jesteś pewien, że nie ma problemu z budowaniem ciągu hh: mm? Może dodaj ten kod do swojego pytania, abyśmy mogli na to spojrzeć. – Windle

+0

Dodałem kilka dodatkowych wyjaśnień, dlaczego tak się stało. Muszę przyznać, że nie jest to zbyt oczywiste. – user3001

Odpowiedz

4

Można spojrzeć na Thread Weaver, lub mogą istnieć inne ramy do testowania wielowątkowego kodu. Nie używałem go, ale Users' Guide wygląda tak, jakby był przeznaczony do dokładnie tego rodzaju testów.

+0

Już to widziałem, ale nie wiem, jak to jest odpowiednie. Doświadczenia z tym, ktoś? – user3001

+0

Próbowałem wątku tkacza. Działa doskonale do testowania, jeśli wiesz, gdzie jest problem. Jednak nie można wykonywać testów N x N nad liniami metody (zapytałem w grupie dyskusyjnej). – user3001

7

Jak wspomniałeś, warunki wyścigów są niezmiernie trudne do odtworzenia w sposób konsekwentny, jednak prawo średnich jest po twojej stronie, jeśli stworzysz test, który prawdopodobnie zakończy się niepowodzeniem, może jeden na sto razy, i następnie spraw, aby zdarzyło się to tysiąc razy, prawdopodobnie prawdopodobnie będziesz dość regularnie złapał błąd w starym kodzie, więc zgodnie z zasadami TDD powinieneś zacząć od kodu w taki sposób, w jaki był wcześniej, wymyślić test wystarczająco często iteruje aby konsekwentnie nie działać zgodnie ze starym kodem przejdź do nowego kodu i upewnij się, że nie zawiedzie.

+0

Możesz mieć duże prawdopodobieństwo niepowodzenia testu, ale większe prawdopodobieństwo, że będziesz wymagał, aby test trwał dłużej. Testy jednostkowe powinny być szybkie. (Ale nie mam lepszego pomysłu.) –

0

Wiem, że ten post jest dość stary, ale mam do czynienia z podobną sytuacją. To, co robię, to faworyzować warunki wyścigu ze snem.

W twoim przypadku, chciałbym zrobić coś takiego

class MyDateTime { 
     String getColonTime() throws InterruptedException{ 
      if (datetime == null) { 
       Thread.sleep(new Random().nextInt(100); //Wait to enhance the chances that multiple threads enter here and reset colonTime. 
       initDateTime(); 
      } 
      Thread.sleep(new Random().nextInt(100); //Wait to enhance the chances that colonTime stays null for a while. 
      if (datetime.colonTime == null) { 
       StringBuilder sb = new StringBuilder(); 
       datetime.colonTime = sb.toString(); 
      } 
      Thread.sleep(new Random().nextInt(100); //Wait to favour reset of colonTime by another thread in the meantime. 
      return datetime.colonTime; 
     } 
    } 

Ale wyraźnie staje się bałagan dość szybko. Szkoda, że ​​nie było sposobu, aby wymusić na programie planującym eksplorację wszystkich ścieżek podanych w niektórych "punktach przerwania".

Ponieważ post jest nieco stary, zastanawiałem się, czy znalazłeś dobre sposoby na przetestowanie warunków wyścigu w Javie. Jakieś rady do dzielenia się?

Dziękujemy

+1

Jak powiedziałem w zaakceptowanej odpowiedzi, tkacz Nici był wspaniałym narzędziem i wydaje mi się, że są teraz inni. Na przykład IntelliJ otrzymał wsparcie debugowania wielowątkowego, jeśli dobrze pamiętam. Również statystyki są po twojej stronie: jeśli często będziesz powtarzać test, na pewno trafisz w stan wyścigu raz na jakiś czas :) Zobacz także https://github.com/junit-team/junit4/wiki/Multithreaded-code i współbieżność – user3001

Powiązane problemy