User-Mode排程

警告

自 Windows 11 起,不支援使用者模式排程。 所有呼叫都會失敗,並出現錯誤 ERROR_NOT_SUPPORTED

使用者模式排程 (UMS) 是一種輕量型機制,可供應用程式用來排程自己的執行緒。 應用程式可以在使用者模式中切換 UMS 執行緒,而不涉及 系統排程器 ,並在核心中的 UMS 執行緒封鎖時重新取得處理器的控制。 UMS 執行緒與 光纖 不同,因為每個 UMS 執行緒都有自己的執行緒內容,而不是共用單一執行緒的執行緒內容。 在使用者模式中切換執行緒的能力,讓 UMS 比 執行緒集 區更有效率,以管理需要少數系統呼叫的大量短期工作專案。

針對高效能需求需要有效率地在多處理器或多核心系統上同時執行許多執行緒的應用程式,建議使用 UMS。 若要利用 UMS,應用程式必須實作排程器元件來管理應用程式的 UMS 執行緒,並判斷何時應該執行。 開發人員應該考慮其應用程式效能需求是否證明開發這類元件所涉及的工作。 允許系統排程器排程其執行緒,可能較適合使用中等效能需求的應用程式。

UMS 適用于在 AMD64 和 Windows 7 和 Windows Server 2008 R2 的 Itanium 版本上執行的 64 位應用程式,透過 Windows 10 版本 21H2 和 Windows Server 2022。 此功能不適用於 Arm64、32 位版本的 Windows 或 Windows 11。

如需詳細資訊,請參閱下列各節:

UMS 排程器

應用程式的 UMS 排程器負責建立、管理和刪除 UMS 執行緒,並判斷要執行的 UMS 執行緒。 應用程式的排程器會執行下列工作:

  • 針對應用程式將執行 UMS 背景工作執行緒的每個處理器,建立一個 UMS 排程器執行緒。
  • 建立 UMS 背景工作執行緒以執行應用程式的工作。
  • 維護自己的就緒執行緒佇列,這些背景工作執行緒已準備好執行,並根據應用程式的排程原則選取要執行的執行緒。
  • 建立並監視一或多個完成清單,其中系統會線上程在核心中完成處理之後排入佇列。 其中包括新建立的背景工作執行緒,以及先前在系統呼叫上遭到封鎖的執行緒。
  • 提供排程器進入點函式來處理來自系統的通知。 系統會在建立排程器執行緒、系統呼叫上的背景工作執行緒區塊或背景工作執行緒明確產生控制權時,呼叫進入點函式。
  • 針對已完成執行的背景工作執行緒執行清除工作。
  • 在應用程式要求時,執行排程器的排序關機。

UMS 排程器執行緒

UMS 排程器執行緒是一般執行緒,透過呼叫 EnterUmsSchedulingMode 函式,將本身轉換成 UMS。 系統排程器會根據相對於其他就緒執行緒的優先順序,判斷 UMS 排程器執行緒何時執行。 排程器執行緒執行的處理器會受到執行緒親和性的影響,與非 UMS 執行緒相同。

EnterUmsSchedulingMode的呼叫端會指定完成清單和UmsSchedulerProc進入點函式,以與 UMS 排程器執行緒產生關聯。 系統完成將呼叫執行緒轉換為 UMS 時,會呼叫指定的進入點函式。 排程器進入點函式負責判斷指定執行緒的適當下一個動作。 如需詳細資訊,請參閱本主題稍後的 UMS 排程器進入點函式

應用程式可能會為每個將用來執行 UMS 執行緒的處理器建立一個 UMS 排程器執行緒。 應用程式可能也會針對特定邏輯處理器設定每個 UMS 排程器執行緒的親和性,這通常會排除不相關的執行緒在該處理器上執行,有效地保留該排程器執行緒。 請注意,以此方式設定執行緒親和性可能會藉由耗盡系統上執行的其他進程,來影響整體系統效能。 如需執行緒親和性的詳細資訊,請參閱 多個處理器

UMS 背景工作執行緒、執行緒內容和完成清單

UMS 背景工作執行緒的建立方式是使用 PROC_THREAD_ATTRIBUTE_UMS_THREAD 屬性呼叫 CreateRemoteThreadEx ,並指定 UMS 執行緒內容和完成清單。

UMS 執行緒內容代表背景工作執行緒的 UMS 執行緒狀態,可用來識別 UMS 函式呼叫中的背景工作執行緒。 其建立方式是呼叫 CreateUmsThreadCoNtext

完成清單是藉由呼叫 CreateUmsCompletionList 函式所建立。 完成清單會接收 UMS 背景工作執行緒,這些執行緒已在核心中完成執行,並準備好在使用者模式中執行。 只有系統可以將背景工作執行緒排入完成清單。 新的 UMS 背景工作執行緒會自動排入建立執行緒時所指定的完成清單。 先前封鎖的背景工作執行緒也會在不再遭到封鎖時排入完成清單。

每個 UMS 排程器執行緒都與單一完成清單相關聯。 不過,相同的完成清單可以與任意數目的 UMS 排程器執行緒相關聯,而排程器執行緒可以從具有指標的任何完成清單中擷取 UMS 內容。

每個完成清單都有一個相關聯的事件,當系統將一或多個背景工作執行緒排入空白清單時,就會發出訊號。 GetUmsCompletionListEvent函式會針對指定的完成清單擷取事件的控制碼。 應用程式可以等候多個完成清單事件,以及其他對應用程式有意義的事件。

UMS 排程器進入點函式

應用程式的排程器進入點函式會實作為 UmsSchedulerProc 函式 。 系統會在下列時間呼叫應用程式的排程器進入點函式:

  • 當非 UMS 執行緒透過呼叫 EnterUmsSchedulingMode轉換成 UMS 排程器執行緒時。
  • UMS 背景工作執行緒呼叫 UmsThreadYield時。
  • 當 UMS 背景工作執行緒封鎖系統服務時,例如系統呼叫或分頁錯誤。

UmsSchedulerProc函式的Reason參數會指定呼叫進入點函式的原因。 如果因為已建立新的 UMS 排程器執行緒而呼叫進入點函式, SchedulerParam 參數會包含 EnterUmsSchedulingMode呼叫端所指定的資料。 如果因為產生 UMS 背景工作執行緒而呼叫進入點函式, SchedulerParam 參數會包含 UmsThreadYield呼叫端所指定的資料。 如果因為核心中封鎖 UMS 背景工作執行緒而呼叫進入點函式, SchedulerParam 參數為 Null。

排程器進入點函式負責判斷指定執行緒的適當下一個動作。 例如,如果封鎖背景工作執行緒,排程器進入點函式可能會執行下一個可用的可用 UMS 背景工作執行緒。

呼叫排程器進入點函式時,應用程式的排程器應該呼叫 DequeueUmsCompletionListItems 函式,嘗試擷取其相關聯完成清單中的所有專案。 此函式會擷取已在核心中完成處理的 UMS 執行緒內容清單,並準備好在使用者模式中執行。 應用程式的排程器不應該直接從此清單中執行 UMS 執行緒,因為這可能會導致應用程式中無法預期的行為。 相反地,排程器應該針對每個內容呼叫 GetNextUmsListItem 函式一次來擷取所有 UMS 執行緒內容、在排程器的就緒執行緒佇列中插入 UMS 執行緒內容,然後只從就緒執行緒佇列執行 UMS 執行緒。

如果排程器不需要等候多個事件,它應該使用非零逾時參數呼叫 DequeueUmsCompletionListItems ,讓函式在傳回之前先等候完成清單事件。 如果排程器需要等候多個完成清單事件,它應該呼叫 DequeueUmsCompletionListItems 且具有逾時參數為零,因此函式會立即傳回,即使完成清單是空的。 在此情況下,排程器可以使用 WaitForMultipleObjects明確等候完成清單事件。

UMS 執行緒執行

新建立的 UMS 背景工作執行緒會排入指定的完成清單,直到應用程式的 UMS 排程器選取要執行為止,才會開始執行。 這與非 UMS 執行緒不同,除非呼叫端明確建立執行緒暫止,否則系統排程器會自動排程執行。

排程器會呼叫 ExecuteUmsThread 與背景工作執行緒的 UMS 內容來執行背景工作執行緒。 UMS 背景工作執行緒會執行,直到呼叫 UmsThreadYield 函式、區塊或終止所產生為止。

UMS 最佳做法

實作 UMS 的應用程式應遵循下列最佳做法:

  • UMS 執行緒內容的基礎結構是由系統管理,不應直接修改。 請改用 QueryUmsThreadInformationSetUmsThreadInformation 來擷取和設定 UMS 背景工作執行緒的相關資訊。
  • 為了協助防止死結,UMS 排程器執行緒不應該與 UMS 背景工作執行緒共用鎖定。 這包括應用程式建立的鎖定和系統鎖定,這些鎖定是透過從堆積配置或載入 DLL 等作業間接取得的。 例如,假設排程器會執行載入 DLL 的 UMS 背景工作執行緒。 背景工作執行緒會取得載入器鎖定和區塊。 系統會呼叫排程器進入點函式,然後載入 DLL。 這會導致死結,因為載入器鎖定已保留,而且在第一個執行緒解除封鎖之前無法釋放。 為避免這個問題,請將可能會與 UMS 背景工作執行緒共用鎖定的工作委派給專用 UMS 背景工作執行緒或非 UMS 執行緒。
  • 在使用者模式中完成大部分處理時,UMS 最有效率。 盡可能避免在 UMS 背景工作執行緒中進行系統呼叫。
  • UMS 背景工作執行緒不應該假設正在使用系統排程器。 此假設可能會有細微的影響;例如,如果未知程式碼中的執行緒設定執行緒優先順序或親和性,UMS 排程器仍可能會覆寫它。 假設系統排程器正在使用的程式碼可能無法如預期般運作,而且在 UMS 執行緒呼叫時可能會中斷。
  • 系統可能需要鎖定 UMS 背景工作執行緒的執行緒內容。 例如,核心模式非同步程序呼叫 (APC) 可能會變更 UMS 執行緒的內容,因此執行緒內容必須鎖定。 如果排程器嘗試在鎖定時執行 UMS 執行緒內容,呼叫將會失敗。 此行為是設計方式,而且排程器應該設計為重試 UMS 執行緒內容的存取權。