Ostatnio pracuję nad projektami, które intensywnie wykorzystują wątki. Myślę, że jestem w porządku przy ich projektowaniu; w jak największym stopniu korzystaj z projektowania bezstanowego, blokuj dostęp do wszystkich zasobów, których potrzebuje więcej niż jeden wątek itp. Moje doświadczenie w programowaniu funkcjonalnym bardzo to pomogło.
Jednak czytając kod wątku innych osób, jestem zdezorientowany. W tej chwili debuguję impas, a ponieważ styl kodowania i projekt różnią się od mojego osobistego stylu, trudno mi dostrzec potencjalne warunki impasu.
Czego szukasz podczas debugowania zakleszczeń?
debugging
multithreading
Michael K.
źródło
źródło
Odpowiedzi:
Jeśli sytuacja jest prawdziwym impasem (tj. Dwa wątki utrzymują dwa różne zamki, ale co najmniej jeden wątek chce blokady, który utrzymuje drugi wątek), musisz najpierw porzucić wszystkie wstępne koncepcje dotyczące sposobu blokowania wątków. Nie zakładaj niczego. Możesz usunąć wszystkie komentarze z kodu, który oglądasz, ponieważ komentarze te mogą sprawić, że uwierzysz w coś, co nie jest prawdą. Trudno to wystarczająco podkreślić: nie zakładaj niczego.
Następnie określ, które blokady będą blokowane, gdy wątek będzie próbował zablokować coś innego. Jeśli możesz, upewnij się, że nić odblokowuje się w odwrotnej kolejności od blokady. Co więcej, upewnij się, że nić posiada tylko jeden zamek na raz.
Starannie przeanalizuj wykonanie wątku i sprawdź wszystkie zdarzenia blokujące. Przy każdej blokadzie ustal, czy wątek zawiera inne blokady, a jeśli tak, to w jakich okolicznościach inny wątek, wykonujący podobną ścieżkę wykonania, może dostać się do rozpatrywanego zdarzenia blokowania.
Z pewnością możliwe jest, że nie znajdziesz problemu, zanim skończy Ci się czas lub pieniądze.
źródło
Jak powiedzieli inni ... jeśli możesz uzyskać przydatne informacje do logowania, spróbuj najpierw, ponieważ jest to najłatwiejsza rzecz do zrobienia.
Zidentyfikuj zaangażowane blokady. Zmień wszystkie muteksy / semafory, które czekają wiecznie, na czas czeka ... coś śmiesznie długiego jak 5 minut. Zaloguj błąd, gdy upłynie limit czasu. To przynajmniej skieruje Cię w stronę jednej z blokad, które są zaangażowane w problem. W zależności od zmienności czasu możesz mieć szczęście i znaleźć obie blokady po kilku biegach. Użyj kodu / warunków błędu funkcji, aby zarejestrować ślad pseudo stosu po tym, jak czas oczekiwania nie określi, w jaki sposób się tam dostałeś. Powinno to pomóc zidentyfikować wątek związany z problemem.
Inną rzeczą, którą możesz wypróbować, jest zbudowanie biblioteki opakowań wokół usług mutex / semaforów. Śledź, jakie wątki mają każdy muteks i jakie wątki czekają na muteks. Utwórz wątek monitorujący, który sprawdza, jak długo wątki blokowały. Uruchom na określony czas i zrzuć śledzone informacje o stanie.
W pewnym momencie konieczna będzie zwykła inspekcja starego kodu.
źródło
Pierwszym krokiem (jak mówi Péter) jest logowanie. Chociaż z mojego doświadczenia jest to często problematyczne. W przypadku ciężkiego przetwarzania równoległego często nie jest to możliwe. Musiałem raz debugować coś podobnego z siecią neuronową, która przetwarzała 100 000 węzłów na sekundę. Błąd pojawił się dopiero po kilku godzinach, a nawet jedna linia wyjściowa spowolniła tak bardzo, że zajęłoby to kilka dni. Jeśli rejestrowanie jest możliwe, skoncentruj się mniej na danych, a bardziej na przebiegu programu, dopóki nie dowiesz się, w której części to się dzieje. Po prostu prosta linia na początku każdej funkcji i jeśli możesz znaleźć odpowiednią funkcję, podziel ją na mniejsze części.
Inną opcją jest usunięcie części kodu i danych w celu zlokalizowania błędu. Może nawet napiszesz mały program, który bierze tylko niektóre klasy i uruchamia tylko najbardziej podstawowe testy (oczywiście w kilku wątkach). Usuń wszystko związane z GUI, na przykład wszelkie dane wyjściowe dotyczące aktualnego stanu przetwarzania. (Często stwierdziłem, że interfejs użytkownika jest źródłem błędu)
W swoim kodzie staraj się postępować zgodnie z pełnym logicznym przepływem kontroli między inicjowaniem blokady a jej zwolnieniem. Częstym błędem może być blokowanie na początku funkcji, odblokowywanie na końcu, ale gdzieś pomiędzy nimi znajduje się warunkowa instrukcja return. Wyjątki mogą również uniemożliwić zwolnienie.
źródło
Moi najlepsi przyjaciele otrzymali wyciągi / dzienniki w interesujących miejscach w kodzie. Zwykle pomagają mi lepiej zrozumieć, co naprawdę dzieje się w aplikacji, bez zakłócania synchronizacji między różnymi wątkami, co może uniemożliwić odtworzenie błędu.
Jeśli to się nie powiedzie, moją jedyną pozostałą metodą jest wpatrywanie się w kod i próba zbudowania modelu mentalnego różnych wątków i interakcji oraz próba wymyślenia możliwych szalonych sposobów na osiągnięcie tego, co najwyraźniej się wydarzyło :-) Ale ja nie uważam się za bardzo doświadczonego pogromcę impasu. Mam nadzieję, że inni będą mogli podać lepsze pomysły, z których również mogę się nauczyć :-)
źródło
Przede wszystkim postaraj się uzyskać autora tego kodu. Prawdopodobnie będzie miał pomysł, co napisał. nawet jeśli dwoje nie potraficie wskazać problemu tylko przez rozmowę, przynajmniej możecie usiąść z nim, aby wskazać część impasu, która będzie znacznie szybsza niż zrozumienie jego / jej kodu bez pomocy.
W przeciwnym razie, jak powiedział Péter Török, rejestrowanie jest prawdopodobnie dobrym rozwiązaniem. O ile mi wiadomo, debugger źle wykonał pracę w środowisku wielowątkowym. spróbuj zlokalizować, gdzie jest zamek, uzyskaj całość, na jakie zasoby czekają iw jakim stanie występują warunki wyścigowe.
źródło
To pytanie mnie pociąga;) Po pierwsze, uważaj się za szczęściarza, ponieważ byłeś w stanie konsekwentnie odtwarzać problem za każdym razem. Jeśli za każdym razem otrzymujesz ten sam wyjątek z tym samym śledzeniem stosu, powinno to być dość proste. Jeśli nie, to nie ufaj tak bardzo stosowi śledzenia, zamiast tego po prostu monitoruj dostęp do obiektów globalnych i jego zmiany stanu podczas wykonywania.
źródło
Jeśli musisz debugować zakleszczenia, już masz kłopoty. Z reguły używaj zamków przez możliwie najkrótszy czas - lub wcale, jeśli to możliwe. Należy unikać każdej sytuacji, w której bierzesz blokadę, a następnie przechodzisz na nietrywialny kod.
Zależy to oczywiście od środowiska programistycznego, ale powinieneś przyjrzeć się takim rzeczom, jak sekwencyjne kolejki, które mogą umożliwić dostęp do zasobu tylko z jednego wątku.
A potem jest stara, ale niezawodna strategia: przypisuj „poziom” do każdej blokady, zaczynając od poziomu 0. Jeśli przejmiesz blokadę poziomu 0, nie będziesz mieć żadnych innych blokad. Po przyjęciu blokady poziomu 1 możesz wziąć blokadę poziomu 0. Po przyjęciu blokady na poziomie 10 możesz wziąć blokady na poziomie 9 lub niższym itp.
Jeśli nie możesz tego zrobić, musisz naprawić swój kod, ponieważ wpadniesz w impas.
źródło