Rozważmy coś w rodzaju aplikacji GUI, w której główny wątek aktualizuje interfejs użytkownika niemal natychmiast, a jakiś inny wątek odpytuje dane w sieci lub coś, co gwarantuje, że ukończenie zadania zajmie 5–10 sekund.
Otrzymałem na to wiele różnych odpowiedzi, ale niektórzy twierdzą, że jeśli jest to wyścigowy warunek statystycznej niemożliwości, nie przejmuj się tym wcale, ale inni powiedzieli, że jeśli jest nawet 10-53 % (żartuję nie na liczbach, oto, co słyszałem) o niektórych magiach voodoo, które dzieją się ze względu na rasę, zawsze uzyskuj / zwalniaj blokady w wątku, który tego potrzebuje.
Jakie są Twoje myśli? Czy dobrą praktyką programistyczną jest radzenie sobie z warunkami wyścigu w takich statystycznie niemożliwych sytuacjach? czy też dodanie dodatkowych linii kodu w celu ograniczenia czytelności byłoby całkowicie niepotrzebne, a nawet przyniosło skutek przeciwny do zamierzonego?
Odpowiedzi:
Jeśli jest to zdarzenie 1 na 10 ^ 55, nie ma potrzeby kodowania tego zdarzenia. Oznaczałoby to, że gdybyś wykonał operację 1 milion razy na sekundę, dostawałbyś jeden błąd co 3 * 10 ^ 41 lat, czyli mniej więcej 10 ^ 31 razy wiek wszechświata. Jeśli Twoja aplikacja ma błąd tylko raz na każde bilion bilionów miliardów wieków wszechświata, to prawdopodobnie jest to wystarczająco wiarygodne.
Jednak postawiłbym bardzo mocno, że błąd nie jest bliski tak mało prawdopodobnego. Jeśli potrafisz wyobrazić sobie błąd, jest prawie pewne, że wystąpi on co najmniej od czasu do czasu, dlatego warto na początku poprawnie go zakodować. Ponadto, jeśli kodujesz wątki poprawnie na początku, aby odpowiednio uzyskać i zwolnić blokady, kod będzie znacznie łatwiejszy do utrzymania w przyszłości. Kiedy robisz zmianę, nie musisz się martwić, że musisz ponownie przeanalizować wszystkie potencjalne warunki wyścigu, ponownie obliczyć ich prawdopodobieństwa i upewnić się, że się nie powtórzą.
źródło
Z punktu widzenia kosztów i korzyści powinieneś napisać dodatkowy kod tylko wtedy, gdy uzyska wystarczającą korzyść.
Na przykład, jeśli najgorszą rzeczą, która miałaby miejsce, gdyby niewłaściwy wątek „wygrał wyścig”, jest to, że informacja nie byłaby wyświetlana, a użytkownik musiałby kliknąć „odśwież”, nie zawracaj sobie głowy chronieniem się przed wyścigiem: pisać dużo kodu nie warto naprawiać czegoś tak nieznaczącego.
Z drugiej strony, jeśli warunek wyścigu może spowodować nieprawidłowe przelewy pieniężne między kontami bankowymi, musisz chronić się przed warunkiem wyścigu bez względu na to, ile kodu musisz napisać, aby rozwiązać ten problem.
źródło
Znalezienie warunków wyścigu jest trudną częścią. Prawdopodobnie poświęciłeś prawie tyle samo czasu na napisanie tego pytania, ile zajęłoby Ci jego naprawienie. To nie tak, że czyni go o wiele mniej czytelnym. Programiści oczekują kodu synchronizacji w takich sytuacjach i mogą tracić więcej czasu, zastanawiając się, dlaczego go nie ma i czy dodanie go naprawi ich niezwiązany błąd.
Jeśli chodzi o prawdopodobieństwa, byłbyś zaskoczony. W zeszłym roku miałem raport o błędzie dotyczący stanu wyścigu, którego nie mogłem odtworzyć dzięki tysiącom zautomatyzowanych prób, ale jeden system jednego klienta widział go cały czas. Wartość biznesowa spędzenia 5 minut na naprawie teraz, w porównaniu z ewentualnym rozwiązaniem problemu „niemożliwego” błędu w instalacji klienta, sprawia, że wybór nie jest trudny.
źródło
Uzyskaj i zwolnij blokady. Prawdopodobieństwa się zmieniają, algorytmy się zmieniają. To zły nawyk, a kiedy coś pójdzie nie tak, nie musisz się zatrzymywać i zastanawiać, czy źle trafiłeś ...
źródło
Dopóki ktoś nie wprowadzi warstwy buforowania w celu poprawy wydajności. Nagle inny bieżnik zakończył się niemal natychmiast, a stan wyścigu objawia się częściej niż nie.
Gdyby dokładnie tak się stało kilka tygodni temu, znalezienie błędu zajęło około 2 pełnych dni programisty.
Zawsze ustalaj warunki wyścigu, jeśli je rozpoznasz.
źródło
Prosty kontra poprawny.
W wielu przypadkach prostota przebija poprawność. To problem z kosztami.
Ponadto warunki wyścigowe to paskudne rzeczy, które zwykle nie przestrzegają prostych statystyk. Wszystko idzie dobrze, dopóki jakaś inna pozornie niezwiązana synchronizacja nie spowoduje, że stan twojej rasy nagle się wydarzy w połowie czasu. O ile nie włączysz dzienników lub nie zdebugujesz kodu.
Pragmatyczną alternatywą dla zapobiegania warunkom wyścigowym (co może być trudne) może być ich wykrycie i zarejestrowanie (premia za ciężkie i wczesne porażki). Jeśli to się nigdy nie zdarzy, niewiele straciłeś. Jeśli tak się stanie, masz solidne uzasadnienie, aby spędzić dodatkowy czas na jego naprawianiu.
źródło
Jeśli twoje warunki rasowe są związane z bezpieczeństwem, zawsze powinieneś kodować, aby temu zapobiec.
Typowym przykładem są warunki wyścigu z tworzeniem / otwieraniem plików w unixie, które w niektórych okolicznościach mogą prowadzić do ataków eskalacji uprawnień, jeśli program z warunkiem wyścigu działa z wyższymi uprawnieniami niż użytkownik wchodzący w interakcję z nim, na przykład proces demona systemu lub co gorsza, jądro.
Nawet jeśli warunek wyścigu ma szansę na losowo 10 ^ (- 80) , być może zdeterminowany atakujący ma przyzwoitą szansę na stworzenie takich warunków celowo i sztucznie.
źródło
Therac-25!
Deweloperzy projektu Therac-25 byli dość pewni co do czasu między interfejsem użytkownika a problemem związanym z interfejsem w terapeutycznej maszynie XRAY.
Nie powinny.
Możesz dowiedzieć się więcej o tej słynnej katastrofie oprogramowania na śmierć i życie pod adresem:
http://www.youtube.com/watch?v=izGSOsAGIVQ
lub
http://en.wikipedia.org/wiki/Therac-25
Twoja aplikacja może być znacznie mniej wrażliwa na awarie niż urządzenia medyczne. Pomocną metodą jest ocena narażenia na ryzyko jako iloczynu prawdopodobieństwa wystąpienia i kosztu wystąpienia w całym okresie użytkowania produktu dla wszystkich jednostek, które można wyprodukować.
Jeśli zdecydowałeś się zbudować swój kod, aby był trwały (i wygląda na to, że masz), powinieneś wziąć pod uwagę prawo Moore'a, które może z łatwością wykasować kilka zer co kilka lat, gdy komputery wewnątrz lub na zewnątrz systemu stają się szybsze. Jeśli wyślesz tysiące kopii, odetnij więcej zer. Jeśli użytkownicy wykonują tę operację codziennie (lub co miesiąc) przez lata, zabierz jeszcze kilka. Jeśli jest używany, gdy dostępne jest włókno Google, co wtedy? Jeśli śmieci interfejsu użytkownika zbierają operacje w trakcie GUI, czy to wpływa na wyścig? Czy korzystasz z biblioteki Open Source lub Windows za GUI? Czy aktualizacje tam mogą wpływać na czas?
Semafory, zamki, muteksy, synchronizacja barier to jedne ze sposobów synchronizacji działań między wątkami. Potencjalnie, jeśli ich nie używasz, inna osoba, która utrzymuje Twój program, może, a następnie dość szybko założyć, że relacje między wątkami mogą się zmienić, a obliczenia dotyczące warunków wyścigu mogą zostać unieważnione.
Zalecam, abyś wyraźnie zsynchronizował, ponieważ chociaż nigdy nie widzisz, że stwarza to problem, klient może. Ponadto, nawet jeśli warunki wyścigu nigdy nie wystąpią, co zrobić, jeśli ty lub Twoja organizacja zostaną wezwani do sądu w celu obrony twojego kodu (ponieważ Toyota była związana z Priusem kilka lat temu). Im dokładniejsza jest twoja metodologia, tym lepiej ci się spodoba. Lepiej byłoby powiedzieć „chronimy się przed takim mało prawdopodobnym przypadkiem jak ten ...” niż powiedzieć „wiemy, że nasz kod zawiedzie, ale spisaliśmy to równanie, aby pokazać, że nie wydarzy się za naszego życia. Prawdopodobnie. „
Wygląda na to, że obliczenie prawdopodobieństwa pochodzi od kogoś innego. Czy znają Twój kod i znasz je na tyle, aby mieć pewność, że nie popełniono błędu? Jeśli policzyłem dla czegoś 99,99997% wiarygodności, mógłbym również wrócić do moich klas statystyki college'u i pamiętać, że nie zawsze otrzymywałem 100% i wycofałem całkiem sporo procent z moich osobistych szacunków wiarygodności.
źródło
Prostota jest dobra tylko wtedy, gdy jest również poprawna. Ponieważ ten kod nie jest prawidłowy, przyszli programiści będą nieuchronnie spojrzeć na to kiedy szuka powiązanego błędu.
Niezależnie od tego, jak sobie z tym poradzisz (poprzez zalogowanie, udokumentowanie lub dodanie blokad - zależy to od kosztu), zaoszczędzisz innym programistom czas patrząc na kod.
źródło
Zależy to od kontekstu. Jeśli jest to zwykła gra na iPhone'a, prawdopodobnie nie. Prawdopodobnie system sterowania lotem do następnego załogowego pojazdu kosmicznego. Wszystko zależy od tego, jakie będą konsekwencje, jeśli zdarzy się „zły” wynik mierzony szacunkowym kosztem jego naprawienia.
Rzadko istnieje odpowiedź „jeden rozmiar dla wszystkich” na tego typu pytania, ponieważ nie są to pytania programistyczne, lecz pytania ekonomiczne.
źródło
Tak, oczekuj nieoczekiwanego. Spędziłem godziny (w kodzie innych osób ^^) na śledzeniu warunków, które nigdy nie powinny się zdarzyć.
Rzeczy takie jak zawsze mają inny, zawsze mają domyślną wielkość liter, inicjują zmienne (tak, naprawdę .. z tego wynikają błędy), sprawdzają pętle pod kątem zmiennych używanych dla każdej iteracji itp.
Jeśli martwisz się problemami związanymi z wątkami, czytaj blogi, artykuły i książki na ten temat. Obecny temat wydaje się niezmiennymi danymi.
źródło
Po prostu to napraw.
Widziałem dokładnie to. Jeden wątek wysyła żądanie sieciowe do serwera, który wyszukuje złożone bazy danych i odpowiada, zanim drugi wątek przejdzie do następnego wiersza kodu. Zdarza się.
Jakiś klient gdzieś zdecyduje, że pewnego dnia uruchomi coś, co pochłonie cały procesor dla „szybkiego” wątku, pozostawiając wolny wątek działający, i będzie ci przykro :)
źródło
Jeśli rozpoznałeś mało prawdopodobny stan wyścigu, przynajmniej udokumentuj go w kodzie!
EDYCJA: Powinienem dodać, że naprawię to, jeśli to w ogóle możliwe, ale w momencie pisania powyższego żadna inna odpowiedź nie powiedziała wprost, że przynajmniej dokumentuje problem w kodzie.
źródło
Myślę, że jeśli już wiesz, jak i dlaczego tak się stało, równie dobrze sobie z tym poradzi. To znaczy, jeśli nie zajmuje dużo zasobów.
źródło
Wszystko zależy od konsekwencji warunków wyścigu. Myślę, że ludzie odpowiadający na twoje pytanie są poprawni w swojej branży. Mój jest silnik konfiguracji routera. Dla mnie warunki wyścigowe powodują, że systemy stoją w miejscu, są uszkodzone lub nieskonfigurowane, nawet jeśli mówi się, że się udało. Zawsze używam semaforów na router, aby nie musiałem niczego ręcznie czyścić.
Myślę, że część mojego kodu GUI nadal jest podatna na warunki wyścigu w taki sposób, że użytkownik może otrzymać błąd, ponieważ wystąpił warunek wyścigu, ale nie miałbym takich możliwości, gdyby istniała szansa na uszkodzenie danych lub niewłaściwe zachowanie podanie po takim zdarzeniu.
źródło
Co zabawne, ostatnio spotkałem ten problem. Nawet nie zdawałem sobie sprawy, że wyścig jest możliwy w moich okolicznościach. Wyścig pojawił się dopiero, gdy procesory wielordzeniowe stały się normą.
Scenariusz był mniej więcej taki. Sterownik urządzenia zgłosił zdarzenia do obsługi przez oprogramowanie. Kontrola musiała jak najszybciej wrócić do sterownika urządzenia, aby zapobiec przekroczeniu limitu czasu w urządzeniu. Aby to zapewnić, zdarzenie zostało zarejestrowane i umieszczone w kolejce w osobnym wątku.
To działało dobrze przez lata. Wtedy nagle zawiodłby w niektórych konfiguracjach. Okazuje się, że wątek kolejkowania działał teraz naprawdę równolegle do wątku obsługi zdarzeń, zamiast współdzielić czas jednego procesora. Udało się wysłać następne polecenie do urządzenia przed potwierdzeniem zdarzenia, co powoduje błąd poza sekwencją.
Biorąc pod uwagę, że wpłynęło to tylko na jednego klienta w jednej konfiguracji, ze wstydem postawiłem
Thread.Sleep(1000)
problem. Od tamtej pory nie było problemu.źródło