Ich möchte eine Aufgabe auslösen, die in einem Hintergrundthread ausgeführt wird. Ich möchte nicht auf die Fertigstellung der Aufgaben warten.
In .net 3.5 hätte ich Folgendes getan:
ThreadPool.QueueUserWorkItem(d => { DoSomething(); });
In .net 4 ist die TPL der vorgeschlagene Weg. Das übliche Muster, das ich empfohlen habe, ist:
Task.Factory.StartNew(() => { DoSomething(); });
Die StartNew()
Methode gibt jedoch ein Task
Objekt zurück, das implementiert wird IDisposable
. Dies scheint von Leuten übersehen zu werden, die dieses Muster empfehlen. In der MSDN-Dokumentation zur Task.Dispose()
Methode heißt es:
"Rufen Sie Dispose immer an, bevor Sie Ihren letzten Verweis auf die Aufgabe freigeben."
Sie können dispose für eine Aufgabe erst dann aufrufen, wenn sie abgeschlossen ist. Wenn Sie also den Hauptthread warten und dispose aufrufen, wird der Sinn eines Hintergrundthreads in erster Linie zunichte gemacht. Es scheint auch kein abgeschlossenes / abgeschlossenes Ereignis zu geben, das für die Bereinigung verwendet werden könnte.
Die MSDN-Seite in der Task-Klasse kommentiert dies nicht, und das Buch "Pro C # 2010 ..." empfiehlt dasselbe Muster und kommentiert die Entsorgung von Aufgaben nicht.
Ich weiß, wenn ich es einfach lasse, wird der Finalizer es am Ende fangen, aber wird das zurückkommen und mich beißen, wenn ich viele Feuer mache und Aufgaben wie diese vergesse und der Finalizer-Thread überfordert ist?
Meine Fragen sind also:
- Ist es in diesem Fall akzeptabel,
Dispose()
dieTask
Klasse nicht anzurufen ? Und wenn ja, warum und gibt es Risiken / Konsequenzen? - Gibt es eine Dokumentation, die dies diskutiert?
- Oder gibt es eine geeignete Möglichkeit, das
Task
Objekt, das ich verpasst habe , zu entsorgen ? - Oder gibt es eine andere Möglichkeit, Fire & Forget-Aufgaben mit der TPL auszuführen?
quelle
Antworten:
Es gibt eine Diskussion darüber in den MSDN - Foren .
Stephen Toub, ein Mitglied des Microsoft Pfx-Teams, hat Folgendes zu sagen:
Update (Okt 2012)
Stephen Toub hat einen Blog mit dem Titel Muss ich Aufgaben entsorgen? Dies gibt einige Details und erklärt die Verbesserungen in .Net 4.5.
Zusammenfassend: Sie müssen
Task
Objekte nicht 99% der Zeit entsorgen .Es gibt zwei Hauptgründe, ein Objekt zu entsorgen: nicht verwaltete Ressourcen rechtzeitig und deterministisch freizugeben und die Kosten für die Ausführung des Finalizers des Objekts zu vermeiden. Beides gilt
Task
meistens nicht für:Task
wird das interne Wartehandle (die einzige nicht verwaltete Ressource imTask
Objekt) nur dann zugewiesen, wenn Sie explizit dasIAsyncResult.AsyncWaitHandle
vonTask
, und verwendenTask
Objekt selbst hat keinen Finalizer. Das Handle selbst ist in ein Objekt mit einem Finalizer eingeschlossen. Wenn es nicht zugewiesen ist, kann kein Finalizer ausgeführt werden.quelle
EndInvoke
in WinForms, wennBeginInvoke
Code auf dem UI-Thread ausgeführt wird). (2) Stephen Toub ist als regelmäßiger Redner über die effektive Nutzung von PFX (z. B. auf channel9.msdn.com ) ziemlich bekannt. Wenn also jemand eine gute Anleitung geben kann, dann ist er es. Beachten Sie seinen zweiten Absatz: Es ist manchmal besser, die Dinge dem Finalisten zu überlassen.Dies ist das gleiche Problem wie bei der Thread-Klasse. Es verbraucht 5 Betriebssystem-Handles, implementiert jedoch kein IDisposable. Gute Entscheidung der ursprünglichen Designer, es gibt natürlich nur wenige vernünftige Möglichkeiten, die Dispose () -Methode aufzurufen. Sie müssten zuerst Join () aufrufen.
Die Task-Klasse fügt diesem einen Handle hinzu, ein internes Ereignis zum manuellen Zurücksetzen. Welches ist die billigste Betriebssystemressource, die es gibt. Natürlich kann die Dispose () -Methode nur dieses eine Ereignishandle freigeben, nicht die 5 Handles, die Thread verwendet. Ja, mach dir keine Sorgen .
Beachten Sie, dass Sie an der IsFaulted-Eigenschaft der Aufgabe interessiert sein sollten. Es ist ein ziemlich hässliches Thema, mehr darüber können Sie in diesem Artikel der MSDN Library lesen . Sobald Sie damit richtig umgehen, sollten Sie auch einen guten Platz in Ihrem Code haben, um die Aufgaben zu entsorgen.
quelle
Thread
in den meisten Fällen keine , sondern verwendet den ThreadPool.Ich würde gerne sehen, wie jemand die in diesem Beitrag gezeigte Technik abwägt : Typasicherer asynchroner Aufruf von Feuer und Vergessen in C #
Es sieht so aus, als würde eine einfache Erweiterungsmethode alle trivialen Fälle der Interaktion mit den Aufgaben behandeln und in der Lage sein, dispose darauf aufzurufen.
quelle
Task
Instanz nicht entsorgtContinueWith
, aber das Zitat von Stephen Toub ist die akzeptierte Antwort: Es gibt nichts zu entsorgen, wenn nichts eine blockierende Wartezeit für eine Aufgabe ausführt.Task disper = null; disper = tsk.ContinueWith(cnt => { cnt.Dispose(); disper.Dispose(); });