Mam kod, który dzieli pewną klasę między 2 wątki i na 100 uruchomień działa dobrze, a już np. 101 się wywala. Wydaje mi się, że zabezpieczyłem wszystko dobrze mutexem. A nadal, bardzo rzadko, ale występuje ten błąd i chciałbym wykryć dlaczego, co, gdzie i jak. Czy jest jakieś narzędzie, które pomogłoby mi w samodzielnym wykryciu tego błędu? Zwykłe przejrzenie callstacku, jak zazwyczaj pomagało, to teraz tak średnio bym powiedział.
- Rejestracja:około 4 lata
- Ostatnio:około 4 lata
- Postów:1

- Rejestracja:ponad 19 lat
- Ostatnio:2 miesiące
Przecież sam napisałeś przyczynę: - "Wydaje mi się, że zabezpieczyłem wszystko dobrze mutexem."
No i masz rację, wydaje ci się.


- Rejestracja:prawie 7 lat
- Ostatnio:3 dni
- Lokalizacja:Kraków
- Postów:1999
Generalnie to wujek Google coś tam wypluwa na temat debugowania aplikacji wielowątkowych, również w kontekście Visual Studio:
https://undo.io/resources/debugging-race-conditions-cpp/
https://docs.microsoft.com/en-us/visualstudio/debugger/debug-multithreaded-applications-in-visual-studio?view=vs-2019
Obawiam się, że szukanie wyścigu na piechotę z wykorzystaniem zwykłego debuggera może być uciążliwe.
Zauważ, że wyścig ogólnie wynika z dwóch rzeczy:
a) występowania zależności (dwa wątki modyfikują i/lub odczytują wspólny zasób)
b) niewłaściwego zabezpieczenia wspólnego zasobu przed jednoczesnym dostępem (brakuje sekcji krytycznej, lub zaczyna się za późno, lub kończy za wcześnie, lub wątki mają przestarzałe kopie i na nich pracują po wyjściu z sekcji krytycznej itp itd)
W efekcie masz sytuację, że jeśli instrukcje w poszczególnych wątkach wykonają się w specyficznej kolejności, to wyścig wystąpi i skutki będą widoczne lub też nie.
Problem jest taki, że w trybie debug:
- pracujesz na niezoptymalizowanym kodzie, zatem może się wykonywać zupełnie inaczej niż finalny kod, optymalizowany w trakcie kompilacji (jak będziesz mieć pecha, wyścig objawi się np. raz na milion wykonań, zamiast raz na 100) - szczególnie, że niezoptymalizowany kod dużo częściej przepisuje z rejestrów do pamięci i z powrotem (zasadniczo dzięki temu możesz np. podglądać wartości zmiennych, rejestrów itd. w trakcie debugowania).
- dodając sobie np. breakpointy i przechodząc po wykonaniu krok po kroku modyfikujesz to wykonanie i znowu, jak będziesz mieć pecha to będzie problem ze zreprodukowaniem wyścigu (patrz punkt wyżej). Zasadniczo może się okazać, że dopiero pauzując wykonanie w specyficznym momencie (żeby wymusić konkretną kolejność wykonania instrukcji z obu wątków) możesz zreprodukować błąd i znaleźć przyczynę. Zauważ, że wątki co do zasady mogą wykonywać się równolegle lub w przeplocie, w przypadku przeplotu OS może wybierać kiedy przełączyć kontekst, dochodzą jeszcze czynniki zewnętrzne (np. jeden z wątków wykonywanych równolegle na innym rdzeniu straci przydział CPU na rzecz zupełnie niepowiązanego procesu).
Ogólnie mając powyższe na uwadze, dwie najbardziej obiecujące / sensowne opcje to:
- raz jeszcze, skrupulatnie przeanalizować kod programu i szczególnie przyjrzeć się wszystkim fragmentom, w których występuje użycie współdzielonego zasobu (instancji danej klasy, jak rozumiem)
- dość obiecująco brzmi (nie korzystałem, nie ocenię) reverse debugging, tj. pozwalasz programowi wykonać się normalnie i w momencie, gdy wystąpi błąd zaczynasz analizować historię wykonania programu, przeglądasz co siedzi w zmiennych, pamięci etc. w poszczególnym momentach ;)

- Rejestracja:prawie 12 lat
- Ostatnio:około 3 godziny
- Lokalizacja:Szczecin
Jak możesz skompilować soft z clangiem/gcc - użyj thread sanitizera




- Rejestracja:około 17 lat
- Ostatnio:2 minuty
JanuszProgramowania123 napisał(a):
Czy jest jakieś narzędzie, które pomogłoby mi w samodzielnym wykryciu tego błędu?
Clang (na MacOS) ma thread sanitizer, używałem działa super.
Nie wiem czy to narzędzie jest dostępne na Linux (gcc i clang) lub Windows.
Na Windows jest Application Verifier (nie używałem).

- Rejestracja:prawie 7 lat
- Ostatnio:3 dni
- Lokalizacja:Kraków
- Postów:1999
MarekR22 napisał(a):
Clang (na MacOS) ma thread sanitizer, używałem działa super.
Nie wiem czy to narzędzie jest dostępne na Linux (gcc i clang) lub Windows.
Może przypadkiem działa lub da się zmusić do działania na Windows, choć w przypadku clang docsy mówią, że nie: https://clang.llvm.org/docs/ThreadSanitizer.html

- Rejestracja:prawie 14 lat
- Ostatnio:około 4 lata
Polecam prześledzić co się działo w momencie crasha na pozostałych wątkach. Jeśli używasz VS to rzuć okiem na Debug > Windows > Parallel Stacks
, ewentualnie Threads
albo Parallel Watch
. W ten sposób możesz (przy odrobinie szczęścia) zawęzić problem do danego kawałka pamięci, o który jest wyścig.
Dorzucę jeszcze kilka narzędzi (poza tymi, o których wspomnieli przedmówcy), przydatnych do analizy data race: