Dieser Artikel wurde maschinell übersetzt.

Windows Mit C++

Threadpool-Abbruch Und - Bereinigung

Kenny Kerr

 

Kenny KerrAbbruch Und Bereinigung Sind Bekanntermaßen Schwierig Probleme Zu Lösen, Wenn es Darum, Multithread-Anwendungen Geht. Wann ist es Sicher, ein Handle Zu schliessen? Ist es Wichtig, Welcher Thread Eine Operation Abgebrochen Wird? Noch Schlimmer Zu Machen, are Some Multithread-APIs Nicht Ablaufinvariant, Wodurch Die Leistung Verbessert, Sondern Auch Zusätzliche Komplexität Für Entwickler.

Ich Habe Die Thread-Pool-Umgebung in des Letzten Monats Eingeführt (msdn.microsoft.com/magazine/hh394144). Eine Wichtige Funktion, die Diese Umgebung wird Bereinigungsgruppen ist, Und Das ist was Ich Werde Hier Konzentrieren. Bereinigungsgruppen installs the you Nicht Alle der Welt Abbruch Und Bereinigung Probleme Zu Lösen. Was Sie Tun ist des Threadpools-Objekte Und Rückrufe Überschaubarer Macht Und Indirekt Damit Kann um Nach Bedarf Abbruch Und Bereinigung von Anderen APIs Und Ressourcen Zu Vereinfachen.

Bisher Habe Ich Nur Ihnen Wie Mit die Unique_handle-Klassenvorlage Arbeitsobjekte Über Die CloseThreadpoolWork-Funktion Automatisch schliessen Gezeigt. (Siehe Spalte August 2011 Auf msdn.microsoft.com/magazine/hh335066 Für Details.) Es Gibt Jedoch Einige Einschränkungen Bei Diesem Ansatz. Soll Ein Sagen, Ob Ausstehende Rückrufe or Nicht Abgebrochen Werden, Müssen Sie Zuerst WaitForThreadpoolWorkCallbacks Aufrufen. Das Macht Zwei Funktionsaufrufen Multipliednumber Callback-Generieren von Objekten in der Verwendung von Ihrer Anwendung. Wenn Sie Sich Entscheiden, TrySubmitThreadpoolCallback Verwenden, Nicht Auch Die Gelegenheit Dazu Bekommen, Und Sie Bleiben Fragen, Wie Sie Abbrechen Oder Warten Sie Auf Den Resultierenden Rückruf. Natürlich Haben Eine Reale Anwendung Wahrscheinlich Weit Mehr als Nur Arbeitsobjekte. Im Nächsten Monat Werde Ich Beginnen, Einführung in Die Anderen Thread-Pool-Objekte, die Rückrufe, Die Vom Zeitgeber Für den i/O um Wait-Fähigen Objekten Zu Erzeugen. Koordinierung der Abbruch Und Bereinigung von all Diesen Kann Schnell Ein Albtraum Werden. Glücklicherweise Gelöst Bereinigungsgruppen Werden Diese Probleme Und Vieles Mehr.

Die CreateThreadpoolCleanupGroup-Funktion Erstellt Ein Bereinigungsgruppenobjekts. Wenn Die Funktion Erfolgreich ist, Gibt es eine Undurchsichtige Zeiger, Die Die Bereinigungsgruppenobjekts repräsentiert. Wenn Sie Nicht Erfolgreich ist, Gibt Sie Den Zeigerwert Null Zurück. Mithilfe von GetLastError Können Sie Detailliertere Informationen Erhalten. Angesichts Ein Bereinigungsgruppenobjekts, Weist Die CloseThreadpoolCleanupGroup-Funktion Threadpool an, ob Das Objekt Freigegeben Werden Kann. Ich Habe Dies Schon Im Vorbeigehen Inhalt, Aber es Trägt Wiederholte – die Threadpool-API Duldet Keine Ungültige Argumente. Mit Einer Ungültigen, Zuvor Geschlossene Funktionen CloseThreadpoolCleanupGroup Oder Keines der Anderen API Aufrufen Oder Null-Zeiger-Wert Bewirkt, Dass die Anwendung Zum Absturz. These are Mängel, Die Durch Den Programmierer Eingeführt Und Sollte Nicht Zur Laufzeit Zusätzliche Prüfungen Erforderlich. Die Unique_handle-Klassenvorlage Ich in Meinem Artikel Vom Juli 2011 Eingeführt (msdn.microsoft.com/magazine/hh288076) Kümmert Sich Diese Informationen Mit Hilfe Einer Klasse Cleanup-Group-eindeutige Merkmale:

struct cleanup_group_traits
{
  static PTP_CLEANUP_GROUP invalid() throw()
  {
    return nullptr;
  }
 
  static void close(PTP_CLEANUP_GROUP value) throw()
  {
    CloseThreadpoolCleanupGroup(value);
  }
};
typedef unique_handle<PTP_CLEANUP_GROUP, cleanup_group_traits> cleanup_group;

Ich Kann Jetzt Die Bequeme Typedef Verwenden Und Erstellen Ein Bereinigungsgruppenobjekts unterstützt:

cleanup_group cg(CreateThreadpoolCleanupGroup());
check_bool(cg);

Wie Bei Privaten Pools Und Prioritäten der Rückruf ist Eine Bereinigungsgruppe Callback-Generieren von Objekten Über Ein Umwelt-Objekt Zugeordnet. Aktualisieren Sie Zunächst die Umgebung der Bereinigungsgruppe ein, Die Die Lebensdauer von Objekten Und Deren Rückrufe Wie Folgt Programmcode:

environment e;
SetThreadpoolCallbackCleanupGroup(e.get(), cg.get(), nullptr);

Eine Dieser Stelle Können Sie Objekte Hinzufügen der Bereinigungsgruppe, die Dann als Mitglieder der Gruppe "Cleanup" Bezeichnet Werden. Diese Objekte Können von der Bereinigungsgruppe Auch Einzeln Entfernt, Aber es ist Üblicher, um Alle Elemente in Einem abzeichnet Vorgang Zu schliessen.

Ein Arbeitsobjekt Kann Mitglied Einer Bereinigungsgruppe Zur Erstellungszeit Werden, Durch Die Bereitstellung der Updated Umgebung Die CreateThreadpoolWork-Funktion:

auto w = CreateThreadpoolWork(work_callback, nullptr, e.get());
check_bool(nullptr != w);

Beachten Sie, Dass Ich Ein Unique_handle Dieses Mal Nicht Verwenden. Das Neu Erstellte Arbeitsobjekt ist Jetzt Mitglied der Bereinigungsgruppe der Umgebung Und seine Lebensdauer Muss Nicht Direkt Mit RAII Nachverfolgt Werden.

Sie Können Das Arbeitsobjekt Mitgliedschaft Entziehen, Nur Durch Schließen, die Auf Wedding Basis Mit der Funktion CloseThreadpoolWork Gemacht Werden Kann. Der Threadpool Weiß, Dass Das Arbeitsobjekt Ein Mitglied der Bereinigungsgruppe ist Und Die Mitgliedschaft Widerruft Vor Dem Schließen. Dadurch Wird Sichergestellt, Dass Die Anwendung Nicht Abstürzen, Wenn der Bereinigungsgruppe Später Versucht, Alle Seine Mitglieder Zu schliessen. Die Umkehrung ist Nicht Wahr: if you, Zuerst Die Bereinigungsgruppe Anweisen Zu Allen Membern Schließen Und Dann CloseThreadpoolWork Für Das Nun Ungültige Arbeitsobjekt Aufrufen, Wird Die Anwendung Abstürzen.

Natürlich ist der Springende Punkt Bei Einer Bereinigungsgruppe Zum Freigeben der Anwendungs Müssen Einzeln Schließen Aller Verschiedenen Callback Generieren-Objekte Kommt es Zu Verwenden. Noch Wichtiger ist, wird es der Anwendung Zu Warten Und optional Alle Ausstehenden Rückrufe in Einem abzeichnet Wartevorgang Abbrechen Anstatt Warten Und Wiederholt Wieder Zu Einem Anwendungsthread Haben Müssen. The CloseThreadpoolCleanupGroupMembers-Funktion provides all Diese Dienste Und Vieles Mehr:

bool cancel = ...
CloseThreadpoolCleanupGroupMembers(cg.get(), cancel, nullptr);

Diese Funktion Kann Einfach Erscheinen, Aber in Wirklichkeit Führt es Eine Reihe von Wichtigen Aufgaben als Aufstellung Aller Seiner Mitglieder. Abhängig Vom Wert des Zweiten Parameters Bricht es Zuerst Alle Ausstehenden Rückrufe, die Noch Auszuführende Angefangen Haben. Anschliessend Wartet er Für Alle Rückrufe, die Bereits Begonnen Haben, Ausführen Und Alle Ausstehenden Rückrufe If you Nicht Abbrechen. Zum Schluss Schließt Alle seine Memberobjekte.

Some Haben Bereinigungsgruppen Art, Garbage Collection, Aber Ich Denke, Das Eine Irreführende Metapher. Wenn Überhaupt, Eine Bereinigungsgruppe ist Eher Wie Ein Container (Standard Template Library, STL) Mit Rückruf Generieren Objekte. Objekte Einer Gruppe Hinzugefügt Werden aus Irgendeinem Grund Nicht Automatisch Geschlossen Werden. Wenn Sie Nicht Zum Aufrufen von CloseThreadpoolCleanupGroupMembers, Wird Ihre Anwendung Speicherverlust Führen. Aufruf CloseThreadpoolCleanupGroup, Schließen Die Gruppe Selbst Wird Entweder Nicht Helfen. You should Stattdessen Nur als Eine Möglichkeit der Verwaltung der Lebensdauer Und Parallelität Eine Gruppe von Objekten Einer Bereinigungsgruppe Vorstellen. Natürlich Können Sie Mehrere Bereinigungsgruppen, Einzeln Verwalten Verschiedene Gruppen von Objekten in der Anwendung Erstellen. Es ist Eine Unglaublich Useful Abstraktion – Aber es Keine Zauberei ist Und Darauf Geachtet Werden Muss, korrekt Verwendet Werden Kann. Berücksichtigen Sie Die Folgenden Fehlerhaften Pseudocode:

environment e;
SetThreadpoolCallbackCleanupGroup(e.get(), cg.get(), nullptr);
 
while (app is running)
{
  SubmitThreadpoolWork(CreateThreadpoolWork(work_callback, nullptr, e.get()));
 
  // Rest of application.
}
 
CloseThreadpoolCleanupGroupMembers(cg.get(), true, nullptr);

Vorhersehbar, Dieser Code Verwendet Eine Unbegrenzte Speichermenge Und Erhalten Langsamer Und Langsamer als Die Systemressourcen Erschöpft Sind.

In Meinem August 2011 Spalte, I Nachgewiesen, Dass Scheinbar easily TrySubmitThreadpoolCallback Funktion tend Problematisch ist, Denn es Keine Einfache Möglichkeit Gibt, Warten, Bis der Rückruf Abgeschlossen. Dies ist da Das Arbeitsobjekt Nicht Tatsächlich von der API Verfügbar Gemacht Wird. Der Threadpool Selbst Leidet Jedoch Keine Solche Einschränkung. Da TrySubmitThreadpoolCallback Einen Zeiger Auf Eine Umgebung Akzeptiert, Können Sie Indirekt Die anfallenden Pauschalsteuer Arbeiten, die Ein Mitglied Einer Bereinigungsgruppe Objekt Vornehmen. Auf Diese Weise CAN you CloseThreadpoolCleanupGroupMembers Warten Oder Brechen Den Resultierenden Rückruf. Betrachten Sie Die folgenden Improved Pseudocode:

environment e;
SetThreadpoolCallbackCleanupGroup(e.get(), cg.get(), nullptr);
 
while (app is running)
{
  TrySubmitThreadpoolCallback(simple_callback, nullptr, e.get());
 
  // Rest of application.
}
 
CloseThreadpoolCleanupGroupMembers(cg.get(), true, nullptr);

Ich Konnte ein Entwickler Für Denken, Ähnlich Wie Garbagecollection, schnelle Vergeben, da der Threadpool Das Arbeitsobjekt Erstellt von TrySubmitThreadpoolCallback Automatisch Geschlossen Wird. Natürlich Hat Stirbt Mit Bereinigungsgruppen Nichts Zu Tun. Ich Beschrieb Dieses Verhalten in Meinem Artikel Vom Juli 2011. Die CloseThreadpoolCleanupGroupMembers-Funktion ist Nicht in Diesem Fall Zunächst Verantwortlich Für Das Arbeitsobjekt, Aber Nur Für Warten Und Rückrufe zeigt Abbrechen Schließen. Im Gegensatz Zum Vorherigen Beispiel Dieser Läuft Auf Unbestimmte Zeit Ohne Unangemessene Ressourcen Und Bieten Noch 
predictable Stornierung Und Bereinigung. Mit Hilfe der Callback-Gruppen Einlöst TrySubmitThreadpoolCallback Selbst, Eine Sichere Und Praktische Alternative. Die in Einer stark Strukturierten Anwendung Wiederholt Die Gleichen Callback Eingereiht Wird, es Wäre Effizienter, Eine Explizite Arbeitsobjekt Wiederverwenden Wobei die Bequemlichkeit der Diese Funktion Kann Nicht Mehr Entlassen Werden.

Bereinigungsgruppen Bieten Eine Endgültige Funktion Zur Vereinfachung des Cleanup-Anforderungen Ihrer Anwendung. Oft Reicht es Nicht Einfach Warten, Ausstehenden Rückrufe Abgeschlossen. Sie Müssen Möglicherweise Einige Cleanuptask Für Jedes Objekt Callback Generieren Ausführen, Nachdem Sie Sich Sicher Keine Weiteren Rückrufe Ausgeführt wird. Mit einer Bereinigungsgruppe Verwaltung der Lebensdauer Dieser Objekte Bedeutet Auch, Dass der Threadpool-ist in der Lage Zu Wissen, Wann Solche Cleanuptasks Geschehen Sollte.

Beim Zuordnen Einer Bereinigungsgruppe Mit Einer Umgebung Durch Die SetThreadpoolCallbackCleanupGroup-Funktion Können Sie Auch Einen Rückruf Für Jedes Mitglied der Bereinigungsgruppe Ausgeführt wird, als Teil des Prozesses der CloseThreadpoolCleanupGroupMembers-Funktion, Schließen Sie Diese Objekte Capabilities. Da es Sich um Ein Attribut der Umgebung Handelt, Können Sie Sogar Verschiedene Rückrufe Auf Unterschiedliche Objekte Gehören der Gleichen Bereinigungsgruppe Anwenden. Im Folgenden Beispiel Erstellen Sie Eine Umgebung Für die Bereinigungsgruppe Und Cleanup Rückruf:

void CALLBACK cleanup_callback(void * context, void * cleanup)
{
  printf("cleanup_callback: context=%s cleanup=%s\n", context, cleanup);
}
 
environment e;
SetThreadpoolCallbackCleanupGroup(e.get(), cg.get(), cleanup_callback);

Der Cleanup-Rückruf Erste Parameter ist der Kontextwert Für Das Generieren Callback-Objekt. Dies ist der Kontextwert, Die, Den Sie Beim Aufrufen der Funktionen CreateThreadpoolWork Oder TrySubmitThreadpoolCallback Angeben, Angenommen, Und es ist, Wie Sie Wissen, welches Objekt, Dass Für der Cleanup-Rückruf Aufgerufen Wird. Der Cleanup-Rückruf Zweite Parameter ist der Wert, der als Letzter Parameter Beim Aufrufen von CloseThreadpoolCleanupGroupMembers-Funktion Enterprise Edition.

Betrachten Sie Nun Die Folgenden Arbeitsobjekte Und Rückrufe:

void CALLBACK work_callback(PTP_CALLBACK_INSTANCE, void * context, PTP_WORK)
{
  printf("work_callback: context=%s\n", context);
}
 
void CALLBACK simple_callback(PTP_CALLBACK_INSTANCE, void * context)
{
  printf("simple_callback: context=%s\n", context);
}
 
SubmitThreadpoolWork(CreateThreadpoolWork(work_callback, "Cheetah", e.get()));
SubmitThreadpoolWork(CreateThreadpoolWork(work_callback, "Leopard", e.get()));
check_bool(TrySubmitThreadpoolCallback(simple_callback, "Meerkat", e.get()));

What this ist Nicht Wie die Anderen? Wie Die Nette Kleine Erdmännchen Ebenso Wie seine Benachbarten Großen Katzen Im Südlichen Afrika Sein Möchte, Wird er Einfach Nie Einer von Ihnen Sein. Was Passiert, Wenn Die Gruppenmitglieder Cleanup Wie Folgt Geschlossen Werden?

CloseThreadpoolCleanupGroupMembers(cg.get(), true, "Cleanup");

Sehr Wenig ist Sicher in Multithreadcode. Wenn die Rückrufe Verwalten, Ausführen, Bevor Sie Abgebrochen Und Geschlossen Sind, zeigt Die Folgenden Ausgedruckt Werden:

work_callback: context=Cheetah
work_callback: context=Leopard
simple_callback: context=Meerkat
cleanup_callback: context=Cheetah cleanup=Cleanup
cleanup_callback: context=Leopard cleanup=Cleanup

Ein Häufiger Fehler ist Anzunehmen, Dass Nur Für Objekte der Cleanup-Rückruf Aufgerufen Wird, Dessen Rückrufe Ausführen Nicht Bekommen. Die Windows-API ist Ein Wenig Irreführend, Weil es Manchmal Bezieht Sich Auf den Cleanup-Rückruf als Rückruf Abbrechen, Aber dies Nicht der Fall ist. Der Cleanup-Rückruf Wird Für Alle Aktuellen Mitglieder der Bereinigungsgruppe Einfach Aufgerufen Werden. Man Könnte es als Einen Destruktor Für Gruppenmitglieder reinigen, Aber Wie Bei der Garbage Collection-Metapher, Stirbt ist Nicht Ohne Risiko. Diese Metapher Hält Ziemlich Gut, Bis Sie an Die Funktion TrySubmitThreadpoolCallback Erhalten Die Wieder Einmal Eine Komplikation Einführt. Denken Sie Daran, Dass der Threadpool Automatisch Das Zugrunde Liegende Arbeitsobjekt Für Die, Das Diese Funktion Erstellt Schließt, Sobald der Rückruf Ausgeführt Wird. Das heisst, Ob der Cleanup-Rückruf Für Dieses Arbeitsobjekt Implizite Führt Hängt Davon Ab, Ob der Rückruf Bereits Begonnen Hat, Zur Ausführung Durch Die Zeit CloseThreadpoolCleanupGroupMembers Aufgerufen Wird. Der Cleanup-Rückruf Wird Für Dieses Arbeitsobjekt Implizite Nur Ausgeführt, Wenn Seine Arbeit Rückruf Noch Anhängig, and you is bitten CloseThreadpoolCleanupGroupMembers, Alle Ausstehenden Rückrufe Abzubrechen. Dies ist Alles Ziemlich Unvorhersehbar Und Daher Nicht Empfehlenswert Einen Cleanup-Rückruf Mit TrySubmitThreadpoolCallback.

Schliesslich ist es Erwähnenswert, Dass, Obwohl der CloseThreadpoolCleanupGroupMembers-Blocks Site Abfälle Nicht der. Alle Objekte, Die Für die Bereinigung Bereitstehen Werden Ihre Cleanup-Rückrufe Auf Dem Aufrufenden Thread Ausgeführt Wird, Während er Darauf Wartet, Dass Andere Ausstehende Rückrufe Abgeschlossen Haben. Die Funktionen von Bereinigungsgruppen – Und Insbesondere der CloseThreadpoolCleanupGroupMembers-Funktion – Sind Für Alle Oder Teile der Anwendung Videoanrufen, Effizient Und Problemlos von Unschätzbarem Wert.

Kenny Kerr* ist Softwarespezialist Mit Dem Schwerpunkt Auf der Systemeigenen Windows-Entwicklung. Sie Erreichen Ihn Unter kennykerr.ca*

Dank der Folgenden Technischen Experten Für die Überprüfung Dieses Artikels: Hari Pulapaka