Udostępnij przez


User-Mode Planowanie

Ostrzeżenie

Od systemu Windows 11 planowanie w trybie użytkownika nie jest obsługiwane. Wszystkie wywołania kończą się niepowodzeniem z powodu błędu ERROR_NOT_SUPPORTED.

Planowanie w trybie użytkownika (UMS) to uproszczony mechanizm, którego aplikacje mogą używać do planowania własnych wątków. Aplikacja może przełączać się między wątkami UMS w trybie użytkownika bez udziału harmonogramu systemu i odzyskać kontrolę nad procesorem, jeśli wątek UMS blokuje w jądrze. Wątki UMS różnią się od światłowodów w tym, że każdy wątek UMS ma własny kontekst wątku zamiast udostępniać kontekst wątku pojedynczego wątku. Możliwość przełączania się między wątkami w trybie użytkownika sprawia, że UMS jest wydajniejsza niż pul wątków do zarządzania dużą liczbą elementów roboczych o krótkim czasie trwania, które wymagają niewielu wywołań systemowych.

Usługa UMS jest zalecana w przypadku aplikacji wymagających wysokiej wydajności, które wymagają wydajnego uruchamiania wielu wątków jednocześnie w systemach wieloprocesorowych lub wielordzeniowych. Aby korzystać z UMS, aplikacja musi zaimplementować składnik harmonogramu, który zarządza wątkami UMS aplikacji i określa, kiedy należy je uruchomić. Deweloperzy powinni rozważyć, czy ich wymagania dotyczące wydajności aplikacji uzasadniają pracę związaną z opracowywaniem takiego składnika. Aplikacje z umiarkowanymi wymaganiami dotyczącymi wydajności mogą być lepiej obsługiwane przez umożliwienie harmonogramowi systemowemu planowania wątków.

Pakiet UMS jest dostępny dla 64-bitowych aplikacji działających w wersjach AMD64 i Itanium systemów Windows 7 i Windows Server 2008 R2 do Windows 10 w wersji 21H2 i Windows Server 2022. Ta funkcja nie jest dostępna w wersji Arm64, 32-bitowej systemu Windows lub Windows 11.

Aby uzyskać szczegółowe informacje, zobacz następujące sekcje:

Harmonogram usługi UMS

Harmonogram UMS aplikacji jest odpowiedzialny za tworzenie i usuwanie wątków pakietu UMS oraz zarządzanie nimi oraz określanie, który wątek pakietu UMS ma być uruchamiany. Harmonogram aplikacji wykonuje następujące zadania:

  • Tworzy jeden wątek harmonogramujący UMS dla każdego procesora, na którym aplikacja będzie uruchamiać wątki robocze UMS.
  • Tworzy wątki robocze UMS w celu realizacji zadań aplikacji.
  • Utrzymuje własną kolejkę gotowych wątków roboczych, które są gotowe do uruchomienia, i wybiera wątki do uruchomienia na podstawie zasad planowania aplikacji.
  • Tworzy i monitoruje co najmniej jedną listę uzupełniania, na której system kolejkuje wątki po zakończeniu przetwarzania w jądrze. Obejmują one nowo utworzone wątki robocze oraz wątki, które były wcześniej zablokowane w wywołaniu systemowym i które zostały odblokowane.
  • Udostępnia funkcję punktu wejścia do harmonogramu w celu obsługi powiadomień z systemu. System wywołuje funkcję punktu wejścia, gdy zostaje utworzony wątek harmonogramu, wątek roboczy blokuje się na wywołaniu systemowym lub wątek roboczy jawnie ustępuje kontrolę.
  • Wykonuje zadania oczyszczania dla wątków roboczych, które zakończyły działanie.
  • Wykonuje uporządkowane zamykanie harmonogramu na żądanie aplikacji.

Wątek harmonogramu UMS

Wątek harmonogramu UMS to zwykły wątek, który przekonwertował się na tryb UMS przez wywołanie funkcji EnterUmsSchedulingMode. Harmonogram systemu określa, kiedy wątek harmonogramu usługi UMS jest uruchamiany na podstawie jego priorytetu względem innych gotowych wątków. Procesor, na którym działa wątek harmonogramu, jest zależny od koligacji wątku, tak jak w przypadku wątków niebędących częścią UMS.

Obiekt wywołujący EnterUmsSchedulingMode określa listę zakończenia oraz funkcję punktu wejścia UmsSchedulerProc do skojarzenia z wątkiem harmonogramu UMS. System wywołuje określoną funkcję punktu wejścia po zakończeniu konwertowania wątku wywołania na UMS. Funkcja punktu wejścia harmonogramu jest odpowiedzialna za określenie odpowiedniej następnej akcji dla określonego wątku. Aby uzyskać więcej informacji, zobacz funkcja punktu wejścia harmonogramu UMS w dalszej części tego tematu.

Aplikacja może utworzyć jeden wątek harmonogramu UMS dla każdego procesora, który będzie używany do uruchamiania wątków usługi UMS. Aplikacja może również ustawić koligację każdego wątku harmonogramu UMS dla określonego procesora logicznego, który zwykle wyklucza niepowiązane wątki z uruchamiania na tym procesorze, skutecznie rezerwując go dla tego wątku harmonogramu. Należy pamiętać, że ustawienie przypisania wątków w ten sposób może wpływać na ogólną wydajność systemu, poprzez ograniczanie zasobów dla innych procesów, które mogą być uruchomione w systemie. Aby uzyskać więcej informacji na temat koligacji wątków, odwiedź wiele procesorów.

Wątki robocze UMS, konteksty wątków i listy ukończenia

Wątek roboczy UMS jest tworzony przez wywołanie CreateRemoteThreadEx z atrybutem PROC_THREAD_ATTRIBUTE_UMS_THREAD oraz określeniem kontekstu wątku UMS i listy ukończenia.

Kontekst wątku UMS reprezentuje stan UMS wątku roboczego i służy do identyfikacji wątku roboczego w wywołaniach funkcji UMS. Jest on tworzony przez wywołanie metody CreateUmsThreadContext.

Lista uzupełniania jest tworzona przez wywołanie funkcji CreateUmsCompletionList. Lista uzupełniania odbiera wątki procesów roboczych UMS, które zakończyły wykonywanie w jądrze i są gotowe do uruchomienia w trybie użytkownika. Tylko system może kolejkować wątki procesów roboczych do listy uzupełniania. Nowe wątki robocze usługi UMS są automatycznie kolejkowane do listy uzupełniania określonej podczas tworzenia wątków. Wcześniej zablokowane wątki robocze są również kolejkowane do listy uzupełniania, gdy nie są już blokowane.

Każdy wątek harmonogramu usługi UMS jest skojarzony z jedną listą uzupełniania. Jednak ta sama lista ukończenia może być skojarzona z dowolną liczbą wątków harmonogramu UMS, a wątek harmonogramu może pobierać konteksty UMS z dowolnej listy ukończenia, dla której ma wskaźnik.

Każda lista uzupełnień ma skojarzone zdarzenie, które jest sygnalizowane przez system, gdy co najmniej jeden wątek roboczy zostaje zakolejkowany do pustej listy. Funkcja GetUmsCompletionListEvent pobiera dojście do zdarzenia dla określonej listy uzupełniania. Aplikacja może czekać na więcej niż jedno zdarzenie listy uzupełniania wraz z innymi zdarzeniami, które mają sens dla aplikacji.

Funkcja punktu wejścia harmonogramu UMS

Funkcja punktu wejścia harmonogramu aplikacji jest implementowana jako funkcja UmsSchedulerProc. System wywołuje funkcję punktu wejścia harmonogramu aplikacji w następujących momentach:

  • Gdy wątek inny niż UMS jest konwertowany na wątek harmonogramu UMS poprzez wywołanie EnterUmsSchedulingMode.
  • Gdy wątek roboczy usługi UMS wywołuje UmsThreadYield.
  • Gdy wątek procesu roboczego usługi UMS blokuje usługę systemową, taką jak wywołanie systemowe lub błąd strony.

Parametr przyczyny funkcji UmsSchedulerProc określa przyczynę, dla której wywołano funkcję punktu wejścia. Jeśli funkcja punktu wejścia została wywołana, ponieważ został utworzony nowy wątek harmonogramu UMS, parametr SchedulerParam zawiera dane określone przez obiekt wywołujący EnterUmsSchedulingMode. Jeśli funkcja punktu wejścia została wywołana, ponieważ wygenerowano wątek procesu roboczego UMS, parametr SchedulerParam zawiera dane określone przez obiekt wywołujący UmsThreadYield. Jeśli wywołano funkcję punktu wejścia, ponieważ wątek UMS zablokował się w jądrze, parametr SchedulerParam ma wartość NULL.

Funkcja punktu wejścia harmonogramu jest odpowiedzialna za określenie odpowiedniej następnej akcji dla określonego wątku. Na przykład jeśli wątek procesu roboczego jest zablokowany, funkcja punktu wejścia harmonogramu może uruchomić następny dostępny gotowy wątek procesu roboczego UMS.

Po wywołaniu funkcji punktu wejścia harmonogramu harmonogram aplikacji powinien podjąć próbę pobrania wszystkich elementów na skojarzonej liście uzupełniania przez wywołanie funkcji DequeueUmsCompletionListItems. Ta funkcja pobiera listę kontekstów wątków pakietu UMS, które zakończyły przetwarzanie w jądrze i są gotowe do działania w trybie użytkownika. Harmonogram aplikacji nie powinien uruchamiać wątków usługi UMS bezpośrednio z tej listy, ponieważ może to spowodować nieprzewidywalne zachowanie w aplikacji. Zamiast tego harmonogram powinien pobrać wszystkie konteksty wątków UMS, wywołując funkcję GetNextUmsListItem raz dla każdego kontekstu, wstawić konteksty wątków UMS do kolejki gotowych wątków harmonogramu, a następnie uruchomić wątki UMS z tej kolejki gotowych wątków.

Jeśli harmonogram nie musi czekać na wiele zdarzeń, powinien wywołać DequeueUmsCompletionListItems z parametrem limitu czasu niezerowego, aby funkcja czekała na zdarzeniu listy ukończenia przed zwróceniem. Jeśli harmonogram musi czekać na wiele zdarzeń listy uzupełniania, powinien wywołać DequeueUmsCompletionListItems z parametrem limitu czasu zero, aby funkcja zwracała natychmiast, nawet jeśli lista uzupełniania jest pusta. W takim przypadku harmonogram może czekać jawnie na zdarzenia listy uzupełniania, na przykład przy użyciu WaitForMultipleObjects.

Wykonywanie wątku UMS

Nowo utworzony wątek procesu roboczego usługi UMS jest kolejkowany do określonej listy uzupełniania i nie zaczyna działać, dopóki harmonogram usługi UMS aplikacji nie wybierze go do uruchomienia. Różni się to od wątków innych niż UMS, które harmonogram systemowy automatycznie planuje uruchomić, chyba że obiekt wywołujący jawnie tworzy wątek zawieszony.

Planista uruchamia wątek roboczy, wywołując ExecuteUmsThread z UMS kontekstem wątku roboczego. Wątek roboczy usługi UMS działa, dopóki nie zostanie zawieszony przez wywołanie funkcji UmsThreadYield, dopóki nie zostanie zablokowany lub zakończy działanie.

Najlepsze rozwiązania dotyczące usługi UMS

Aplikacje implementujące usługę UMS powinny przestrzegać następujących najlepszych rozwiązań:

  • Podstawowe struktury kontekstów wątków usługi UMS są zarządzane przez system i nie powinny być modyfikowane bezpośrednio. Zamiast tego użyj QueryUmsThreadInformation i SetUmsThreadInformation, aby pobrać i ustawić informacje o wątku roboczym UMS.
  • Aby zapobiec zakleszczeniom, wątek harmonogramu usługi UMS nie powinien udostępniać blokad wątkom roboczym usługi UMS. Obejmuje to zarówno blokady utworzone przez aplikację, jak i blokady systemowe, które są uzyskiwane pośrednio przez operacje, takie jak przydzielanie ze sterty lub ładowanie bibliotek DLL. Załóżmy na przykład, że planista uruchamia wątek roboczy UMS, który ładuje bibliotekę DLL. Wątek roboczy uzyskuje blokadę modułu ładującego i zatrzymuje się. System wywołuje funkcję punktu wejścia harmonogramu, która następnie ładuje bibliotekę DLL. Powoduje to zakleszczenie, ponieważ blokada modułu ładującego jest już utrzymywana i nie można jej zwolnić do momentu, gdy pierwszy wątek się odblokuje. Aby uniknąć tego problemu, deleguj pracę, która może udostępniać blokady wątkom roboczym usługi UMS do dedykowanego wątku roboczego usługi UMS lub wątku innego niż UMS.
  • Usługa UMS jest najbardziej wydajna, gdy większość przetwarzania odbywa się w trybie użytkownika. Jeśli to możliwe, unikaj wykonywania wywołań systemowych w wątkach procesów roboczych usługi UMS.
  • Wątki robocze UMS nie powinny zakładać, że jest używany systemowy planista. To założenie może mieć subtelne skutki; na przykład jeśli wątek w nieznanym kodzie ustawia priorytet wątku lub koligację, harmonogram UMS może nadal go zastąpić. Kod, który zakłada, że harmonogram systemu jest używany, może nie zachowywać się zgodnie z oczekiwaniami i może przerwać, gdy jest wywoływany przez wątek UMS.
  • System może wymagać zablokowania kontekstu wątku roboczego UMS. Na przykład wywołanie procedury asynchronicznej trybu jądra (APC) może zmienić kontekst wątku UMS, więc kontekst wątku musi być zablokowany. Jeśli harmonogram spróbuje wykonać kontekst wątku UMS, gdy jest on zablokowany, wywołanie zakończy się niepowodzeniem. To zachowanie jest zaprojektowane zgodnie z projektem, a harmonogram powinien zostać zaprojektowany tak, aby ponowić próbę dostępu do kontekstu wątku usługi UMS.