Threadpools

Ein Threadpool ist eine Sammlung von Arbeitsthreads, die asynchrone Rückrufe im Auftrag der Anwendung effizient ausführen. Der Threadpool wird hauptsächlich verwendet, um die Anzahl der Anwendungsthreads zu reduzieren und die Verwaltung der Arbeitsthreads bereitzustellen. Anwendungen können Arbeitselemente warteschlangen, Arbeiten mit wartenden Handlen zuordnen, automatisch Warteschlange basierend auf einem Zeitgeber und bindung mit I/O.

Threadpoolarchitektur

Die folgenden Anwendungen können von der Verwendung eines Threadpools profitieren:

  • Eine Anwendung, die hoch parallel ist und eine große Anzahl kleiner Arbeitselemente asynchron senden kann (z. B. verteilte Indexsuche oder Netzwerk-I/O).
  • Eine Anwendung, die eine große Anzahl von Threads erstellt und zerstört, die jeweils kurz ausgeführt werden. Die Verwendung des Threadpools kann die Komplexität der Threadverwaltung und den Aufwand verringern, der bei der Threaderstellung und -zerstörung beteiligt ist.
  • Eine Anwendung, die unabhängige Arbeitselemente im Hintergrund und parallel verarbeitet (z. B. Laden mehrerer Registerkarten).
  • Eine Anwendung, die eine exklusive Wartezeit auf Kernelobjekte ausführen muss oder auf eingehenden Ereignissen auf einem Objekt blockieren muss. Die Verwendung des Threadpools kann die Komplexität der Threadverwaltung verringern und die Leistung erhöhen, indem die Anzahl der Kontextschalter reduziert wird.
  • Eine Anwendung, die benutzerdefinierte Warterthreads erstellt, um auf Ereignisse zu warten.

Der ursprüngliche Threadpool wurde in Windows Vista vollständig neu erstellt. Der neue Threadpool wird verbessert, da es einen einzelnen Arbeitsthreadtyp (unterstützt sowohl I/O als auch nicht-I/O) bereitstellt, kein Zeitgeberthread verwendet, eine einzelne Zeitgeberwarteschlange bereitstellt und einen dedizierten beständigen Thread bereitstellt. Darüber hinaus bietet es Bereinigungsgruppen, höhere Leistung, mehrere Pools pro Prozess, die unabhängig geplant werden, und eine neue Threadpool-API.

Die Threadpoolarchitektur besteht aus folgendem:

  • Arbeitsthreads, die die Rückruffunktionen ausführen
  • Waiter-Threads, die auf mehrere Wartenhandpunkte warten
  • Eine Arbeitswarteschlange
  • Ein Standardthreadpool für jeden Prozess
  • Eine Arbeitsfabrik, die die Arbeitsthreads verwaltet

Bewährte Methoden

Die neue Threadpool-API bietet mehr Flexibilität und Kontrolle als die ursprüngliche Threadpool-API. Es gibt jedoch einige subtile, aber wichtige Unterschiede. In der ursprünglichen API war das Warten zurücksetzen automatisch; in der neuen API muss die Wartezeit jedes Mal explizit zurückgesetzt werden. Die ursprüngliche API behandelt automatisch den Identitätswechsel, übertragen den Sicherheitskontext des Aufrufvorgangs in den Thread. Mit der neuen API muss die Anwendung den Sicherheitskontext explizit festlegen.

Die folgenden methoden sind bewährte Methoden beim Verwenden eines Threadpools:

  • Die Threads eines Prozesses teilen den Threadpool. Ein einzelner Arbeitsthread kann mehrere Rückruffunktionen gleichzeitig ausführen. Diese Arbeitsthreads werden vom Threadpool verwaltet. Beenden Sie daher keinen Thread aus dem Threadpool, indem Sie "TerminateThread " im Thread aufrufen oder exitThread aus einer Rückruffunktion aufrufen.

  • Eine I/O-Anforderung kann auf jedem Thread im Threadpool ausgeführt werden. Das Abbrechen von I/O auf einem Threadpoolthread erfordert die Synchronisierung, da die Abbrechenfunktion möglicherweise auf einem anderen Thread ausgeführt wird als der, der die I/O-Anforderung behandelt, was zu einer Abbruch eines unbekannten Vorgangs führen kann. Um dies zu vermeiden, stellen Sie immer die ÜBERLAPPENDE Struktur bereit, mit der eine I/O-Anforderung beim Aufrufen von CancelIoEx für asynchrone I/O initiiert wurde, oder verwenden Sie ihre eigene Synchronisierung, um sicherzustellen, dass keine andere I/O im Zielthread gestartet werden kann, bevor sie entweder die CancelSynchronousIo- oder CancelIoEx-Funktion aufrufen.

  • Bereinigen Sie alle Ressourcen, die in der Rückruffunktion erstellt wurden, bevor Sie aus der Funktion zurückkehren. Dazu gehören TLS, Sicherheitskontexte, Threadpriorität und COM-Registrierung. Rückruffunktionen müssen auch den Threadzustand wiederherstellen, bevor sie zurückgegeben werden.

  • Warten Sie die Ziehpunkte und ihre zugeordneten Objekte, bis der Threadpool signalisiert hat, dass sie mit dem Handle abgeschlossen ist.

  • Markieren Sie alle Threads, die auf lange Vorgänge warten (z. B. I/O-Spülungen oder Ressourcenbereinigung), sodass der Threadpool neue Threads zuweisen kann, anstatt auf diesen Zu warten.

  • Bevor Sie eine DLL entladen, die den Threadpool verwendet, kündigen Sie alle Arbeitselemente, I/O, Wartezeiten und Zeitgeber, und warten Sie, bis Rückrufe ausgeführt werden.

  • Vermeiden Sie Deadlocks, indem Abhängigkeiten zwischen Arbeitselementen und zwischen Rückrufen entfernt werden, indem sichergestellt wird, dass ein Rückruf nicht auf sich selbst wartet, und indem sie die Threadpriorität beibehalten.

  • Warteschlange nicht zu viele Elemente zu schnell in einem Prozess mit anderen Komponenten mithilfe des Standardthreadpools. Es gibt einen Standardthreadpool pro Prozess, einschließlich Svchost.exe. Standardmäßig verfügt jeder Threadpool über maximal 500 Arbeitsthreads. Der Threadpool versucht, mehr Arbeitsthreads zu erstellen, wenn die Anzahl der Arbeitsthreads im bereit/ausgeführten Zustand kleiner als die Anzahl der Prozessoren sein muss.

  • Vermeiden Sie das COM-Einzelthread-Apartmentmodell, da sie nicht mit dem Threadpool kompatibel ist. STA erstellt Threadstatus, der sich auf das nächste Arbeitselement für den Thread auswirken kann. STA ist im Allgemeinen langlebig und verfügt über Threadaffinität, was das Gegenteil des Threadpools ist.

  • Erstellen Sie einen neuen Threadpool, um die Threadpriorität und -isolation zu steuern, benutzerdefinierte Merkmale zu erstellen und möglicherweise die Reaktionsfähigkeit zu verbessern. Zusätzliche Threadpools erfordern jedoch mehr Systemressourcen (Threads, Kernelspeicher). Zu viele Pools erhöhen das Potenzial für CPU-Inhalte.

  • Wenn möglich, verwenden Sie ein wartende Objekt anstelle eines APC-basierten Mechanismus, um einen Threadpoolthread zu signalisieren. APCs funktionieren nicht genauso gut mit Threadpoolthreads wie andere Signalmechanismen, da das System die Lebensdauer von Threadpoolthreads steuert, sodass es möglich ist, dass ein Thread beendet werden kann, bevor die Benachrichtigung übermittelt wird.

  • Verwenden Sie die Threadpool-Debuggererweiterung !tp. Dieser Befehl verfügt über die folgende Verwendung:

    • Pooladressenflags
    • obj addressflags
    • tqueue addressflags
    • Warteradresse
    • Arbeitsadresse

    Wenn die Adresse null ist, wird für Pool, Waiter und Worker die Befehlsabbildung aller Objekte angezeigt. Wenn Sie warten und arbeiten, werden die Adressabbilde des aktuellen Threads weggelassen. Die folgenden Flags sind definiert: 0x1 (Einzelzeilenausgabe), 0x2 (Dumpelemente) und 0x4 (Dumppoolarbeitswarteschlange).

Threadpool-API

Verwenden der Threadpoolfunktionen