線程集區

線程集區是背景工作線程的集合,可有效率地代表應用程式執行異步回呼。 線程集區主要用於減少應用程式線程數目,並提供背景工作線程的管理。 應用程式可以將工作專案排入佇列、將工作與可等候的句柄產生關聯、根據定時器自動排入佇列,以及與 I/O 系結。

線程集區架構

下列應用程式可以受益於使用線程集區:

  • 高度平行且可非同步分派大量小型工作項目的應用程式(例如分散式索引搜尋或網路 I/O)。
  • 建立並終結大量線程的應用程式,每個線程會執行一小段時間。 使用線程集區可以降低線程管理的複雜度,以及線程建立和解構所涉及的額外負荷。
  • 在背景和平行處理獨立工作專案的應用程式(例如載入多個索引標籤)。
  • 應用程式,必須在核心對象上執行獨佔等候,或封鎖物件上的傳入事件。 使用線程集區可減少線程管理的複雜性,並藉由減少內容交換器數目來提升效能。
  • 建立自定義等候程式線程以等候事件的應用程式。

原始線程集區已在 Windows Vista 中完全重新架構。 新的執行緒集區之所以有所改良,是因為它提供單一的工作執行緒類型(同時支援 I/O 和非 I/O)、不使用計時器執行緒、提供單一的計時器佇列,並提供專用的常駐執行緒。 它也提供清理群組、更高的效能、每個程序可有多個獨立排程的集區,以及新的執行緒集區 API。

線程集區架構包含下列各項:

  • 執行回呼函式的工作執行緒
  • 等候多個等候句柄的等候者線程
  • 工作佇列
  • 每個進程的預設線程集區
  • 管理背景工作線程的背景工作處理站

最佳做法

新的 線程集區 API 提供比 原始線程集區 API 更多的彈性和控制。 然而,有一些微妙但重要的差異。 在原始 API 中,等候重設是自動的;在新 API 中,每次都必須明確重設等候。 原始 API 會自動處理模擬作業,將呼叫處理序的安全性脈絡轉移到執行緒。 使用新的 API 時,應用程式必須明確設定安全性內容。

以下是使用線程集區時的最佳做法:

  • 程序的執行緒會共用執行緒集區。 單一工作執行緒可以一次一個地執行多個回呼函式。 這些工作執行緒由執行緒集區管理。 因此,請勿在線程上呼叫 TerminateThread,或從回調函式呼叫 ExitThread,以終止線程集區中的線程。

  • I/O 要求可以在線程集區中的任何線程上執行。 取消執行緒集區執行緒上的 I/O 需要同步,因為取消函式可能會在與處理 I/O 要求的執行緒不同的執行緒上執行,這可能會導致某個未知的作業遭到取消。 若要避免這種情況,當針對非同步 I/O 呼叫 CancelIoEx 時,請一律提供用來起始 I/O 要求的 OVERLAPPED 結構;或者使用您自己的同步機制,以確保在呼叫 CancelSynchronousIoCancelIoEx 函式之前,不會在目標執行緒上啟動任何其他 I/O。

  • 在從函式返回之前,請清理回呼函式中建立的所有資源。 其中包括 TLS、安全性內容、線程優先順序和 COM 註冊。 回呼函式也必須在傳回之前還原線程狀態。

  • 讓等待控制代碼及其相關物件維持存活,直到執行緒集區發出已完成該控制代碼使用的訊號為止。

  • 標示等候冗長作業的所有線程(例如 I/O 排清或資源清除),讓線程集區可以配置新的線程,而不是等待這個線程。

  • 在解除載入使用執行緒集區的 DLL 之前,請先取消所有工作項目、I/O、等待作業和計時器,並等待正在執行的回呼完成。

  • 藉由消除工作項目之間以及回呼之間的相依性、確保回呼不會等待自身完成,並保留執行緒優先順序,來避免死結。

  • 請勿在與其他元件共用預設執行緒集區的處理程序中,過快地將太多項目加入佇列。 每個進程都有一個預設線程集區,包括Svchost.exe。 預設情況下,每個執行緒集區最多有 500 個工作執行緒。 當處於就緒/執行中狀態的背景工作線程數目必須小於處理器數目時,線程集區會嘗試建立更多背景工作線程。

  • 避免 COM 單個線程 Apartment 模型,因為它與線程集區不相容。 STA 會建立可能會影響線程下一個工作項目的線程狀態。 STA 通常壽命很長,而且具有線程親和性,這與線程集區相反。

  • 建立新的線程集區來控制線程優先順序和隔離、建立自定義特性,並可能改善回應性。 不過,其他線程集區需要更多系統資源(線程、核心記憶體)。 太多集區會增加 CPU 爭用的可能性。

  • 如果可以,請使用可等待物件,而不要使用以 APC 為基礎的機制來向執行緒集區執行緒發出訊號。 APC 與其他訊號機制相比,較不適合用於執行緒集區執行緒,因為系統會控制執行緒集區執行緒的生命週期,所以執行緒有可能在通知送達前就終止。

  • 使用線程集區調試程式延伸模組 !tp。 此指令具有下列用法:

    • 集區位址旗標
    • obj 位址旗標
    • tqueue 位址旗標
    • waiter 位址
    • 工作程序 位址

    如果位址為零,則針對集區、服務員和背景工作角色,命令會傾印所有物件。 對於 waiter 和 worker,省略位址會傾印目前執行緒。 已定義下列旗標:0x1(單行輸出)、0x2(傾印成員)和0x4(傾印集區工作佇列)。

Note

現代替代方案:對於 C++/WinRT 應用程式,Windows 執行緒池可透過協程存取——用於co_await winrt::resume_background()將工作派遣至執行緒池,並co_await winrt::resume_foreground(dispatcher)返回 UI 執行緒。 這提供結構化的並行性,無需手動回調管理。 對於標準 C++ 程式碼, std::async with std::launch::async policy 提供可攜式的任務基礎平行處理,但不會直接使用 Windows 執行緒池。

線程集區 API

使用線程集區函式