Die Klasse CancellationTokenSource
ist verfügbar. Ein kurzer Blick in Reflector zeigt die Verwendung KernelEvent
einer (sehr wahrscheinlich) nicht verwalteten Ressource. Da CancellationTokenSource
es keinen Finalizer gibt, wird der GC dies nicht tun, wenn wir ihn nicht entsorgen.
Wenn Sie sich dagegen die Beispiele ansehen, die im MSDN-Artikel Abbrechen in verwalteten Threads aufgeführt sind , verfügt nur ein Code-Snippet über das Token.
Was ist der richtige Weg, um es im Code zu entsorgen?
- Sie können Code, mit dem
using
Sie Ihre parallele Aufgabe starten, nicht umbrechen, wenn Sie nicht darauf warten. Und eine Stornierung ist nur dann sinnvoll, wenn Sie nicht warten. - Natürlich können Sie
ContinueWith
mit einemDispose
Anruf eine Aufgabe hinzufügen , aber ist das der richtige Weg? - Was ist mit stornierbaren PLINQ-Abfragen, die nicht zurücksynchronisieren, sondern am Ende nur etwas tun? Sagen wir
.ForAll(x => Console.Write(x))
? - Ist es wiederverwendbar? Kann dasselbe Token für mehrere Aufrufe verwendet werden und dann zusammen mit der Hostkomponente entsorgt werden, z. B. UI-Steuerung?
Da es so etwas wie eine Reset
Bereinigungs- IsCancelRequested
und Token
Feldmethode nicht gibt, würde ich annehmen, dass es nicht wiederverwendbar ist. Daher sollten Sie jedes Mal, wenn Sie eine Aufgabe (oder eine PLINQ-Abfrage) starten, eine neue erstellen. Ist es wahr? Wenn ja, ist meine Frage, welche Strategie in Dispose
diesen vielen CancellationTokenSource
Fällen richtig und empfohlen ist .
quelle
Important: The CancellationTokenSource class implements the IDisposable interface. You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.
- docs.microsoft.com/en-us/dotnet/standard/threading/…Ich fand keine der aktuellen Antworten zufriedenstellend. Nach Recherchen fand ich diese Antwort von Stephen Toub ( Referenz ):
Der kühne Teil, denke ich, ist der wichtige Teil. Er verwendet "wirkungsvoller", was es etwas vage lässt. Ich interpretiere es so, dass das Aufrufen
Dispose
in solchen Situationen erfolgen sollte, andernfalls ist die VerwendungDispose
nicht erforderlich.quelle
Ich habe in ILSpy nach dem gesucht,
CancellationTokenSource
aber ich kann nur herausfinden,m_KernelEvent
welches tatsächlich ein istManualResetEvent
, welches eine Wrapper-Klasse für einWaitHandle
Objekt ist. Dies sollte vom GC ordnungsgemäß gehandhabt werden.quelle
Sie sollten immer entsorgen
CancellationTokenSource
.Wie es zu entsorgen ist, hängt genau vom Szenario ab. Sie schlagen verschiedene Szenarien vor.
using
Funktioniert nur, wenn SieCancellationTokenSource
parallele Arbeiten ausführen, auf die Sie warten. Wenn das dein Senario ist, dann ist es großartig, es ist die einfachste Methode.Verwenden
ContinueWith
Sie bei der Verwendung von Aufgaben eine Aufgabe, die Sie angegeben haben, um sie zu entsorgenCancellationTokenSource
.Für plinq können Sie verwenden,
using
da Sie es parallel ausführen, aber darauf warten, dass alle parallel ausgeführten Mitarbeiter fertig sind.Für die Benutzeroberfläche können Sie
CancellationTokenSource
für jede stornierbare Operation eine neue erstellen , die nicht an einen einzelnen Abbruchauslöser gebunden ist. Pflegen Sie aList<IDisposable>
und fügen Sie jede Quelle zur Liste hinzu. Entsorgen Sie alle, wenn Ihre Komponente entsorgt wird.Erstellen Sie für Threads einen neuen Thread, der alle Worker-Threads verbindet und die einzelne Quelle schließt, wenn alle Worker-Threads abgeschlossen sind. Siehe CancellationTokenSource, Wann entsorgen?
Es gibt immer einen Weg.
IDisposable
Instanzen sollten immer entsorgt werden. Beispiele sind oft nicht vorhanden, weil sie entweder schnelle Beispiele sind, um die Kernnutzung zu zeigen, oder weil das Hinzufügen aller Aspekte der demonstrierten Klasse für ein Beispiel zu komplex wäre. Die Stichprobe ist nur eine Stichprobe, nicht unbedingt (oder sogar normalerweise) Produktionsqualitätscode. Es ist nicht akzeptabel, dass alle Muster unverändert in den Produktionscode kopiert werden.quelle
await
für die Aufgabe nicht verwenden und die CancellationTokenSource in dem Code entsorgen konnten, der nach dem Warten kommt?await
einer Operation abgebrochen wird , können Sie aufgrund eines Vorgangs fortfahrenOperationCanceledException
. Sie könnten dann anrufenDispose()
. Wenn jedoch noch Vorgänge ausgeführt werden und das entsprechende verwendet wirdCancellationToken
, wird dieses Token weiterhinCanBeCanceled
als vorhanden gemeldettrue
, obwohl die Quelle entsorgt ist. Wenn sie versuchen, einen Stornierungsrückruf zu registrieren, wird BOOM! ,ObjectDisposedException
. Es ist sicher genug,Dispose()
nach erfolgreichem Abschluss der Operation (en) anzurufen . Es wird sehr schwierig, wenn Sie tatsächlich etwas abbrechen müssen.Diese Antwort taucht immer noch in der Google-Suche auf, und ich glaube, dass die abgestimmte Antwort nicht die ganze Geschichte wiedergibt. Nachdem ich mir den Quellcode für
CancellationTokenSource
(CTS) undCancellationToken
(CT) angesehen habe, glaube ich, dass für die meisten Anwendungsfälle die folgende Codesequenz in Ordnung ist:Das
m_kernelHandle
oben erwähnte interne Feld ist das Synchronisationsobjekt, das dieWaitHandle
Eigenschaft sowohl in der CTS- als auch in der CT-Klasse unterstützt. Es wird nur instanziiert, wenn Sie auf diese Eigenschaft zugreifen. Wenn Sie also keineWaitHandle
Thread-Synchronisierung der alten Schule in IhrerTask
aufrufenden Entsorgung verwenden, hat dies keine Auswirkungen.Wenn Sie es verwenden, sollten Sie natürlich das tun, was in den anderen Antworten oben vorgeschlagen wird, und den Aufruf verzögern,
Dispose
bis alleWaitHandle
Vorgänge mit dem Handle abgeschlossen sind, da die Ergebnisse , wie in der Windows-API-Dokumentation für WaitHandle beschrieben , undefiniert sind.quelle
IsCancellationRequested
Eigenschaft des Tokens durch Abfragen, Rückrufen oder Warten." Mit anderen Worten: Möglicherweise verwenden nicht Sie (dh derjenige, der die asynchrone Anforderung stellt) das Wartehandle, sondern möglicherweise der Listener (dh derjenige, der die Anforderung beantwortet). Dies bedeutet, dass Sie als Verantwortlicher für die Entsorgung praktisch keine Kontrolle darüber haben, ob der Wartegriff verwendet wird oder nicht.Es ist lange her, dass ich dies gefragt und viele hilfreiche Antworten erhalten habe, aber ich bin auf ein interessantes Problem gestoßen und dachte, ich würde es hier als eine andere Art von Antwort posten:
Sie sollten
CancellationTokenSource.Dispose()
nur anrufen , wenn Sie sicher sind, dass niemand versuchen wird, dasToken
Eigentum des CTS zu erhalten. Ansonsten solltest du es nicht nennen, weil es ein Rennen ist. Zum Beispiel hier:https://github.com/aspnet/AspNetKatana/issues/108
In der Korrektur für dieses Problem wurde der Code, der zuvor
cts.Cancel(); cts.Dispose();
bearbeitet wurde, nur bearbeitet,cts.Cancel();
weil jeder, der so viel Pech hatte, das Stornierungs-Token zu erhalten, um seinen Stornierungsstatus nach demDispose
Aufruf zu beobachten, leider auch behandelt werden mussObjectDisposedException
- zusätzlich zu demOperationCanceledException
dass sie geplant hatten.Eine weitere wichtige Bemerkung zu diesem Fix wurde von Tratcher gemacht: "Die Entsorgung ist nur für Token erforderlich, die nicht storniert werden, da die Stornierung alle die gleiche Bereinigung bewirkt." dh nur zu tun
Cancel()
statt zu entsorgen ist wirklich gut genug!quelle
Ich habe eine thread-sichere Klasse erstellt, die a
CancellationTokenSource
an a bindetTask
und garantiert, dass die KlasseCancellationTokenSource
entsorgt wird, wenn die zugehörigeTask
abgeschlossen ist. Es verwendet Schlösser, um sicherzustellen, dass dasCancellationTokenSource
Produkt während oder nach der Entsorgung nicht gelöscht wird. Dies geschieht zur Einhaltung der Dokumentation , in der Folgendes angegeben ist:Und auch :
Hier ist die Klasse:
Die primären Methoden der
CancelableExecution
Klasse sind dieRunAsync
und dieCancel
. Standardmäßig sind gleichzeitige Vorgänge nicht zulässig, dh das AufrufenRunAsync
ein zweites stillschweigend abgebrochen wird und auf den Abschluss des vorherigen Vorgangs (sofern dieser noch ausgeführt wird) gewartet wird, bevor der neue Vorgang gestartet wird.Diese Klasse kann in Anwendungen jeglicher Art verwendet werden. Die Hauptverwendung erfolgt jedoch in UI-Anwendungen, in Formularen mit Schaltflächen zum Starten und Abbrechen eines asynchronen Vorgangs oder in einem Listenfeld, das einen Vorgang jedes Mal abbricht und neu startet, wenn das ausgewählte Element geändert wird. Hier ist ein Beispiel für den ersten Fall:
Die
RunAsync
Methode akzeptiert ein ExtraCancellationToken
als Argument, das mit dem intern erstellten verknüpft istCancellationTokenSource
. Das Bereitstellen dieses optionalen Tokens kann in Fortschrittsszenarien hilfreich sein.quelle