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

KaRzye14 sierpnia 20264 min czytaniaZaktualizowano 4 października
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
Sesja na żywo, zdalnieZdalna optymalizacja PC pod gry z pomiarem przed i po

Łączymy się z Twoim komputerem zdalnie, zapisujemy punkt przywracania i przechodzimy przez zasilanie, sterowniki i input. BIOS ustawiamy razem na rozmowie na Discordzie, kiedy pokazujesz nam ekran telefonem. Ten sam benchmark na końcu pokazuje, ile z tego wyszło. Przez pół roku po sesji zostajemy z Tobą na Discordzie.

Wybierz pakiet →od 150 zł · 60 min+

Jak wykrywać deadlocki w Windows

  1. Menedżer zadań, zakładka Szczegóły. Kliknij proces prawym przyciskiem i wybierz Analizuj łańcuch oczekiwania. Okno pokazuje, na czym czeka wybrany proces.
  2. Process Explorer (Sysinternals). W Open Handles szukaj nazw mutexów, plików i obiektów COM trzymanych zbyt długo.
  3. PerfMon. Liczniki Thread State oraz Thread Wait Reason, czyli stan wątku i powód jego oczekiwania. Wartość 5 w Thread State oznacza oczekiwanie.
  4. Zrzut procesu i WinDbg. Zapisz zrzut zawieszonego procesu i przejrzyj stosy 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.SRWLOCK lock = SRWLOCK_INIT; AcquireSRWLockShared(&lock); ReleaseSRWLockShared(&lock);
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 wysokim tick-rate serweraUż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 czeka w nieskończoność. Sprawdź to opcją Analizuj łańcuch oczekiwania.
  • PerfMon. Thread State wątków gry nie pozostaje na wartości 5 (Wait) przez cały pomiar.
  • Serwer. Średni tick-rate serwera zgodny z jego ustawieniem, 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 spadają dokładnie wtedy, gdy zaczyna się walka?

Ten spadek wypada w tej jednej sekundzie, która rozstrzyga rundę, i żaden preset grafiki tego nie załatwi. Windows, sterowniki i sieć ustawiamy zdalnie, a BIOS razem z Tobą na rozmowie na Discordzie. Pomiar robisz sam, w swojej grze, przed i po, przy nas.

Zobacz optymalizację PC →Sześć miesięcy wsparcia po sesji. Nie wiesz, czy warto? Poproś najpierw o darmowe sprawdzenie na naszym Discordzie.
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