Deadlocki w Windows: jak je wykryć i jak ich unikać

KaRzye14 sierpnia 20263 min czytaniaZaktualizowano 21 sierpnia
Dark UltimateMove banner reading When two threads wait forever, with two blocked locks in a cycle

Gra zamarza na dwie sekundy w środku pojedynku. FPS pokazuje 240, a obraz stoi. Serwer gubi tick, mimo że procesor się nudzi. Za częścią takich przypadków stoi deadlock, czyli zakleszczenie wątków. Ten artykuł pokazuje, jak je rozpoznać i jak się ich pozbyć.

Co to jest deadlock

Deadlock od Valve… dużo o nim piszemy – niestety tym razem chodzi o coś mniej kolorowego – Deadlock systemowy, po polsku zakleszczenie, występuje wtedy, gdy dwa wątki czekają nawzajem na zasoby, które już sobie zablokowały. Wątek A trzyma blokadę 1 i czeka na blokadę 2. Wątek B trzyma blokadę 2 i czeka na blokadę 1. Żaden nie ustąpi, więc oba stoją do końca życia procesu.

Z perspektywy gracza nie widać żadnego obciążenia. Procesor ma zapas, karta graficzna ma zapas, a mimo to nic się nie rusza.

Gdzie to boli w grach i na serwerach

ObszarPrzykład w grachKonsekwencje
KlientBlokada na wątku renderowania, gdy klatka czeka na dane z sieciZacięcie albo czarny ekran przy wysokim FPS
SerwerLogika gry czeka na wynik z innego wątku, na przykład z AIOpóźnienia w synchronizacji stanu, spadek tick-rate
MatchmakingAlgorytm dobierający graczy czeka na dane blokowane przez inny procesDłuższe kolejki, więcej timeoutów przy przydziale mapy
Optymalizacja UltimateTen sam łańcuch, tylko że ktoś siedzi obok

BIOS, pamięć, sieć i input przechodzimy razem na Twoim ekranie. Pomiar przed i po robimy w Twoich grach, więc różnicę widzisz sam, zanim się rozłączymy.

Wybierz pakiet od 150 zł · 60 min+

Jak wykrywać deadlocki w Windows

  1. Menedżer zadań, zakładka Szczegóły. Kolumna Blocked pokazuje procesy zatrzymane przez blokadę.
  2. Process Explorer (Sysinternals). W Open Handles szukaj nazw mutexów, plików i obiektów COM trzymanych zbyt długo.
  3. PerfMon. Liczniki MutexWait Time oraz ThreadTimeInState(4), czyli czas w stanie oczekiwania.
  4. ETW i Debugging Tools. Włącz dostawcę Microsoft-Windows-Debugger i przejrzyj stosy w WinDbg poleceniem ~* k. Wątki zakleszczone stoją na tym samym WaitForSingleObject przez cały zrzut.

Skąd się biorą

  • Różna kolejność zakładania blokad w różnych wątkach
  • Operacja I/O wykonywana wewnątrz sekcji krytycznej
  • Mieszanie apartamentów COM, czyli wywołania synchroniczne między STA i MTA
  • Odwrócenie priorytetów, gdy wątek o niskim priorytecie trzyma blokadę potrzebną wątkowi o wysokim

Jak ich unikać

StrategiaCo zrobićPrzykładowy kod (C++/Win32)
Lock orderingUstal jedną kolejność zakładania blokad i trzymaj się jej w całym kodzie, na przykład alfabetycznie po nazwie.void AcquireAll(std::vector<MutexHandle>& locks){ std::sort(locks.begin(), locks.end(), [](auto& a, auto& b){ return a.name < b.name; }); for(auto& l : locks) WaitForSingleObject(l.handle, INFINITE); }
Try-lock z timeoutemJeśli nie zdobędziesz blokady w zadanym czasie, zwolnij to, co już trzymasz, i spróbuj od nowa.bool TryLock(MutexHandle& m){ return WaitForSingleObject(m.handle, 500) == WAIT_OBJECT_0; }
Nie blokuj głównego wątkuWątki UI i renderowania trzymaj wolne. I/O rób asynchronicznie.// odczyt asynchroniczny przez strukturę overlapped ReadFile(hFile, buf, size, NULL, &ov); // żadnych blokujących wywołań w sekcji krytycznej
SRWLOCK zamiast mutexaWielu czytelników pracuje równolegle, a wyłączność dostaje tylko zapis.std::shared_mutex srw; AcquireShared(srw); ReleaseShared(srw);
Higiena apartamentów COMUI w STA, logika serwera w MTA. Unikaj synchronicznych wywołań między nimi.CoInitializeEx(NULL, COINIT_MULTITHREADED);
Watchdog wykrywający cykleCo kilka sekund sprawdzaj graf oczekiwań. Cykl oznacza zakleszczenie, więc zrestartuj wątek albo aplikację.Przeszukiwanie w głąb (DFS) po grafie wait-for

Konkretne przypadki z serwerów gier

ProblemRozwiązanie
Matchmaking czeka na bazę danych (SQL Server)Skróć transakcje, przejrzyj plany zapytań, włącz READ COMMITTED SNAPSHOT.
Wątek AI trzyma mutex przez całe obliczeniaPrzenieś obliczenia poza sekcję krytyczną i zwolnij blokadę zaraz po zapisie wyniku.
Synchronizacja stanu gry przy tick-rate 128Użyj SpinLock zamiast Mutex, ale trzymaj sekcję krytyczną w granicach kilku mikrosekund.

Co z tego ma gracz

  • Krótsze opóźnienia. Usunięcie zakleszczenia zdejmuje z czasu odpowiedzi serwera te milisekundy, których nie widać w żadnym benchmarku.
  • Stabilna sesja. Klient przestaje zamarzać w losowych momentach, a wykres FPS przestaje mieć dziury.
  • Lepsza skalowalność. Serwer obsłuży więcej graczy na tym samym sprzęcie.

Checklista po wdrożeniu zmian

  • Menedżer zadań. Przy dużym obciążeniu żaden proces nie siedzi w stanie Blocked.
  • PerfMon. MutexWait Time poniżej 5 ms dla aplikacji klienckich.
  • Serwer. Średni tick-rate co najmniej 128, bez gubionych ticków.
  • Profiler. Intel VTune albo AMD uProf nie pokazuje długich sekcji krytycznych.

Podsumowanie

Zakleszczenie nie zużywa zasobów, więc nie zobaczysz go w żadnym wykresie obciążenia. Szukaj go tam, gdzie wątki stoją: w Menedżerze zadań, w Process Explorerze i w zrzutach stosu.

Trzy nawyki wystarczą, żeby ich nie produkować. Ustal jedną kolejność blokad. Trzymaj I/O poza sekcjami krytycznymi. Na serwerze sięgaj po struktury lock-free, a po stronie klienta po asynchroniczne I/O. Efekt widać w pingu, w stabilności klatek i w liczbie graczy, których serwer utrzyma bez zadławienia.


Powiązane artykuły


Klatki spadaja dokladnie wtedy, gdy zaczyna sie walka?

Deadlock mocno obciaza procesor w teamfightach i to wlasnie tam tracisz plynnosc, a razem z nia celnosc. Ustawiamy komputer zdalnie: Windows, sterowniki, BIOS i siec. Pomiar przed i po mozesz zrobic sam, w grze, przy nas. Nie blokujemy tego.

Zobacz optymalizacje PCGwarancja satysfakcji i szesc miesiecy wsparcia po sesji. Nie wiesz, czy warto? Popros o darmowe sprawdzenie na naszym Discordzie.
K KaRzye Prowadzi research i wszystkie sesje w UltimateMove. FACEIT Level 10 w CS2, Immortal w Valorancie. Dwadzieścia lat w BIOS-ie, pamięciach i sterownikach. Zobacz jak gra.

Zostaw Komentarz

KaRzye - Gaming logo - Twitchtv channel

„KaRzye” - CS2 Faceit lvl 10, trener Immortal Valorant oraz ekspert w dziedzinie optymalizacji komputerów PC z ponad 15-letnim doświadczeniem. Od 2006 roku zajmuje się podkręcaniem i optymalizacją komputerów do gier.

Expertyza w grach:

Ultimate Deadlock Replay Analyzer
Deadlock Stream Overlay

Koszyk