Sdílet prostřednictvím


User-Mode plánování

Varování

Od Windows 11 se plánování uživatelského režimu nepodporuje. Všechna volání selžou s chybou ERROR_NOT_SUPPORTED.

Plánování uživatelského režimu (UMS) je jednoduchý mechanismus, pomocí kterého můžou aplikace plánovat vlastní vlákna. Aplikace může přepínat mezi vlákny UMS v uživatelském režimu, aniž by zahrnovala systémový plánovač a znovu získat kontrolu nad procesorem, pokud vlákno UMS blokuje v jádru. Vlákna UMS se liší od vláken tím, že každé vlákno UMS má svůj vlastní kontext vlákna, na rozdíl od sdílení kontextu sdíleného vlákna. Díky schopnosti přepínat mezi vlákny v uživatelském režimu je UMS účinnější než fondy vláken pro správu mnoha krátkodobých pracovních úloh, které vyžadují málo systémových volání.

Doporučuje se UMS pro aplikace s vysokými nároky na výkon, které potřebují efektivně spouštět mnoho vláken paralelně na multiprocesorových nebo vícejádrových systémech. Aby mohla aplikace využívat výhod UMS, musí implementovat komponentu plánovače, která spravuje vlákna UMS aplikace a určuje, kdy se mají spustit. Vývojáři by měli zvážit, jestli požadavky na výkon aplikace odůvodňují práci, která je součástí vývoje takové komponenty. Aplikace s mírnými požadavky na výkon můžou být lépe obsluhovány tím, že plánovači systému umožní naplánovat svá vlákna.

Služba UMS je k dispozici pro 64bitové aplikace běžící na procesorech AMD64 a Itanium ve verzích Windows 7 a Windows Server 2008 R2 až Windows 10 verze 21H2 a Windows Server 2022. Tato funkce není dostupná v Arm64, 32bitových verzích Windows nebo Ve Windows 11.

Podrobnosti najdete v následujících částech:

Plánovač UMS

Plánovač UMS aplikace zodpovídá za vytváření, správu a odstraňování vláken UMS a určení, které vlákno UMS se má spustit. Plánovač aplikace provádí následující úlohy:

  • Vytvoří jedno vlákno plánovače UMS pro každý procesor, na kterém bude aplikace spouštět pracovní vlákna UMS.
  • Vytvoří pracovní vlákna UMS pro provádění práce aplikace.
  • Udržuje vlastní frontu připravených pracovních vláken, která jsou připravena ke spuštění, a vybírá vlákna, která se mají spustit na základě zásad plánování aplikace.
  • Vytvoří a monitoruje jeden nebo více seznamů dokončení, do kterých systém řadí vlákna poté, co dokončí zpracování v jádru. Patří mezi ně nově vytvořená pracovní vlákna a vlákna dříve blokovaná v systémovém volání, která se odblokují.
  • Poskytuje funkci vstupního bodu plánovače pro zpracování oznámení ze systému. Systém volá funkci vstupního bodu, když je vytvořeno vlákno plánovače, pracovní vlákno je blokováno systémovým voláním, nebo když pracovní vlákno explicitně předává kontrolu.
  • Provádí úkoly úklidu pro pracovní vlákna, která dokončila svou činnost.
  • Provede řádné vypnutí plánovače při vyžádání aplikací.

Vlákno plánovače UMS

Vlákno plánovače UMS je obyčejné vlákno, které se převedlo samo sebe na UMS voláním funkce EnterUmsSchedulingMode. Systémový plánovač určuje, kdy se vlákno plánovače UMS spouští na základě priority vzhledem k jiným připraveným vláknům. Procesor, na kterém běží vlákno plánovače, je ovlivněno spřažením vlákna, stejně jako u vláken mimo UMS.

Volající EnterUmsSchedulingMode určuje seznam výsledků a vstupní funkci UmsSchedulerProc, která se má přidružit k vláknu plánovače UMS. Systém volá zadanou funkci vstupního bodu po dokončení převodu volajícího vlákna na UMS. Funkce vstupního bodu plánovače zodpovídá za určení odpovídající další akce pro zadané vlákno. Další informace najdete v tématu Funkce vstupního bodu plánovače UMS dále v tomto tématu.

Aplikace může vytvořit jedno vlákno plánovače UMS pro každý procesor, který se použije ke spouštění vláken UMS. Aplikace může také nastavit spřažení každého vlákna plánovače UMS pro konkrétní logický procesor, který má tendenci vyloučit nesouvisející vlákna z provozu na daném procesoru a efektivně ho rezervuje pro toto vlákno plánovače. Mějte na paměti, že nastavení spřažení vláken tímto způsobem může ovlivnit celkový výkon systému odčerpáním zdrojů jiným procesům, které mohou být spuštěny v systému. Další informace o spřažení vláken naleznete v tématu více procesorů.

Pracovní vlákna UMS, kontexty vláken a seznamy dokončení

Pracovní vlákno UMS se vytvoří voláním CreateRemoteThreadEx s atributem PROC_THREAD_ATTRIBUTE_UMS_THREAD a zadáním kontextu vlákna UMS a seznamu dokončení.

Kontext vlákna UMS představuje stav pracovního vlákna UMS a slouží k identifikaci pracovního vlákna ve voláních funkce UMS. Je vytvořen voláním CreateUmsThreadContext.

Seznam dokončení je vytvořen voláním CreateUmsCompletionList funkce. Seznam dokončení obdrží pracovní vlákna UMS, která dokončila provádění v jádru a jsou připravená ke spuštění v uživatelském režimu. Pouze systém může zařadit pracovní vlákna do fronty v seznamu dokončení. Nová pracovní vlákna UMS se automaticky zařadí do seznamu dokončení, který byl zadán při vytváření těchto vláken. Dříve blokovaná pracovní vlákna jsou do seznamu dokončení zařazena také, pokud už nejsou blokovaná.

Každé vlákno plánovače Jednotného zasílání zpráv je přidružené k jednomu seznamu dokončení. Stejný seznam dokončení lze však přidružit k libovolnému počtu vláken plánovače UMS a vlákno plánovače může načíst kontexty UMS z libovolného seznamu dokončení, pro který má ukazatel.

Každý seznam pro dokončení má přidruženou událost, která je signalizována systémem, když systém zařadí jedno nebo více pracovních vláken do seznamu, který je prázdný. Funkce GetUmsCompletionListEvent načte popisovač události pro zadaný seznam dokončení. Aplikace může čekat na více než jednu událost seznamu dokončení spolu s dalšími událostmi, které pro aplikaci mají smysl.

Funkce vstupního bodu plánovače UMS

Funkce vstupního bodu plánovače aplikace je implementována jako funkce UmsSchedulerProc. Systém volá funkci vstupního bodu plánovače aplikace v následujících časech:

  • Pokud je vlákno mimo UMS převedeno na vlákno plánovače UMS voláním EnterUmsSchedulingMode.
  • Když pracovní vlákno UMS volá UmsThreadYield.
  • Když pracovní vlákno UMS zablokuje systémovou službu, jako je například systémové volání nebo chyba stránky.

Parametr Reason funkce UmsSchedulerProc určuje důvod, proč byla volána funkce pro vstupní bod. Pokud byla funkce vstupního bodu volána kvůli vytvoření nového vlákna plánovače UMS, parametr SchedulerParam obsahuje data určená volajícím EnterUmsSchedulingMode. Pokud byla volána funkce vstupního bodu z důvodu uvolnění pracovního vlákna UMS, parametr SchedulerParam obsahuje data určená volajícím funkci UmsThreadYield. Pokud byla volána funkce vstupního bodu, protože pracovní vlákno UMS bylo blokováno v jádru, parametr SchedulerParam má hodnotu NULL.

Funkce vstupního bodu plánovače zodpovídá za určení odpovídající další akce pro zadané vlákno. Pokud je například zablokované pracovní vlákno, může funkce vstupního bodu plánovače spustit další dostupné pracovní vlákno UMS.

Při zavolání funkce vstupního bodu plánovače by se plánovač aplikace měl pokusit pomocí funkce DequeueUmsCompletionListItems načíst všechny položky ze svého přidruženého seznamu dokončení. Tato funkce načte seznam kontextů vlákna UMS, které dokončily zpracování v jádru, a jsou připravené ke spuštění v uživatelském režimu. Plánovač aplikace by neměl spouštět vlákna UMS přímo z tohoto seznamu, protože to může způsobit nepředvídatelné chování v aplikaci. Místo toho by plánovač měl načíst všechny kontexty vlákna UMS voláním funkce GetNextUmsListItem jednou pro každý kontext, vložte kontexty vlákna UMS do fronty vláken připravené plánovače a teprve potom spusťte vlákna UMS z připravené fronty vláken.

Pokud plánovač nemusí čekat na více událostí, měl by volat DequeueUmsCompletionListItems s nenulovým parametrem časového limitu, aby funkce čekala na událost seznamu dokončení před vrácením. Pokud plánovač potřebuje čekat na více událostí seznamu dokončení, měl by volat DequeueUmsCompletionListItems s parametrem časového limitu nuly, aby funkce vrátila okamžitě, i když je seznam dokončení prázdný. V tomto případě může plánovač výslovně čekat na události ze seznamu dokončení, například pomocí WaitForMultipleObjects.

Provádění vláken UMS

Nově vytvořené pracovní vlákno UMS se zařadí do fronty do zadaného seznamu dokončení a nezačne se spouštět, dokud ho plánovač UMS aplikace nevybere ke spuštění. Liší se od vláken jiných než typu UMS, která plánovač systému automaticky naplánuje ke spuštění, pokud volající nevytvoří vlákno explicitně ve stavu pozastavení.

Plánovač spouští pracovní vlákno voláním ExecuteUmsThread s kontextem UMS pracovního vlákna. Pracovní vlákno UMS běží, dokud se buď neuvolní voláním funkce UmsThreadYield, nebo se nezablokuje, či neukončí.

Osvědčené postupy pro UMS

Aplikace, které implementují jednotné zasílání zpráv, by měly dodržovat tyto osvědčené postupy:

  • Základní struktury kontextů vláken UMS spravuje systém a neměly by být upraveny přímo. Místo toho použijte QueryUmsThreadInformation a SetUmsThreadInformation k načtení a nastavení informací o pracovním vlákně UMS.
  • Aby se zabránilo zablokování, nemělo by vlákno plánovače UMS sdílet zámky s pracovními vlákny UMS. To zahrnuje zámky vytvořené aplikací i zámky systému, které jsou získány nepřímo operacemi, jako je přidělení z haldy nebo načítání knihoven DLL. Předpokládejme například, že plánovač spouští pracovní vlákno UMS, které načte knihovnu DLL. Pracovní vlákno získá zámek zavaděče a je zablokováno. Systém volá funkci vstupního bodu plánovače, která pak načte dynamickou knihovnu DLL. To způsobí mrtvý zámek, protože zámek zavaděče je již držen a nelze ho uvolnit, dokud se první vlákno neuvolní. Chcete-li se tomuto problému vyhnout, delegujte práci, která by mohla sdílet zámky s pracovními vlákny UMS na vyhrazené pracovní vlákno UMS nebo vlákno jiné než UMS.
  • UmS je nejúčinnější, když se provádí zpracování v uživatelském režimu. Kdykoli je to možné, vyhněte se volání systému v pracovních vláknech UMS.
  • Pracovní vlákna UMS by neměla předpokládat, že se používá systémový plánovač. Tento předpoklad může mít jemné dopady; například, pokud vlákno v neznámém kódu nastaví prioritu vlákna nebo afinitu, plánovač UMS ho může stále přepsat. Kód, který předpokládá, že se používá systémový plánovač, se nemusí chovat podle očekávání a může dojít k přerušení při zavolání vláknem UMS.
  • Systém může potřebovat uzamknout kontext pracovní vlákna UMS. Například asynchronní volání procedury v režimu jádra (APC) může změnit kontext vlákna UMS, takže kontext vlákna musí být uzamčen. Pokud se plánovač pokusí během uzamčení spustit kontext vlákna UMS, volání selže. Toto chování je záměrně a plánovač by měl být navržený tak, aby zkusil znovu přístup k kontextu vlákna UMS.