Chcę uruchomić zadanie w wątku w tle. Nie chcę czekać na zakończenie zadań.
W .net 3.5 zrobiłbym to:
ThreadPool.QueueUserWorkItem(d => { DoSomething(); });
W .net 4 TPL jest sugerowanym sposobem. Typowy zalecany wzór, który widziałem, to:
Task.Factory.StartNew(() => { DoSomething(); });
Jednak StartNew()
metoda zwraca Task
obiekt, który implementuje IDisposable
. Wydaje się, że jest to przeoczane przez osoby, które zalecają ten wzór. Dokumentacja MSDN dotycząca Task.Dispose()
metody mówi:
„Zawsze wywołuj Dispose, zanim zwolnisz ostatnie odniesienie do zadania”.
Nie możesz wywołać dispose na zadaniu, dopóki nie zostanie ono zakończone, więc pozostawienie głównego wątku oczekiwania i wywołania dispose pokonałoby w pierwszej kolejności punkt robienia w wątku w tle. Wydaje się również, że nie ma żadnego zakończonego / zakończonego wydarzenia, które można by wykorzystać do czyszczenia.
Strona MSDN w klasie Task nie komentuje tego, a książka „Pro C # 2010 ...” zaleca ten sam wzorzec i nie komentuje usuwania zadań.
Wiem, że jeśli po prostu zostawię to, finalizator w końcu to złapie, ale czy to wróci i mnie ugryzie, gdy będę wykonywać wiele zadań typu „odpal i zapomnij”, a wątek finalizatora zostanie przeciążony?
Więc moje pytania to:
- Czy dopuszczalne jest, aby nie dzwonić
Dispose()
naTask
klasy w tym przypadku? A jeśli tak, dlaczego i czy istnieje ryzyko / konsekwencje? - Czy jest jakaś dokumentacja, która to omawia?
- A może istnieje odpowiedni sposób na pozbycie się
Task
przedmiotu, który przegapiłem? - A może istnieje inny sposób wykonywania zadań „odpal i zapomnij” z TPL?
źródło
Odpowiedzi:
Jest dyskusja na ten temat na forach MSDN .
Stephen Toub, członek zespołu Microsoft pfx, ma do powiedzenia:
Aktualizacja (październik 2012)
Stephen Toub opublikował blog zatytułowany Czy muszę pozbyć się zadań? co zawiera więcej szczegółów i wyjaśnia ulepszenia w .Net 4.5.
Podsumowując: nie musisz pozbywać się
Task
przedmiotów w 99% przypadków.Istnieją dwa główne powody, dla których należy pozbyć się obiektu: w celu zwolnienia niezarządzanych zasobów w wyznaczonym czasie i w celu uniknięcia kosztów uruchamiania finalizatora obiektu. Żadne z nich nie ma zastosowania przez
Task
większość czasu:Task
przydziela wewnętrzny uchwyt wait (jedyny niekontrolowana zasobów wTask
obiekcie) jest wtedy, gdy wyraźnie używaćIAsyncResult.AsyncWaitHandle
zTask
iTask
obiekt nie ma finalizatora; sam uchwyt jest opakowany w obiekt z finalizatorem, więc jeśli nie zostanie przydzielony, nie ma finalizatora do uruchomienia.źródło
EndInvoke
w WinForms podczas używaniaBeginInvoke
do uruchamiania kodu w wątku interfejsu użytkownika). (2) Stephen Toub jest dość dobrze znany jako regularny mówca na temat efektywnego wykorzystania PFX (np. Na channel9.msdn.com ), więc jeśli ktoś może udzielić dobrych wskazówek, to on jest tym. Zwróć uwagę na jego drugi akapit: są chwile, gdy pozostawiasz sprawy finalizatorowi, a jest lepiej.Jest to ten sam problem, co w przypadku klasy Thread. Zużywa 5 uchwytów systemu operacyjnego, ale nie implementuje IDisposable. Dobra decyzja pierwotnych projektantów, oczywiście istnieje kilka rozsądnych sposobów wywołania metody Dispose (). Najpierw musisz zadzwonić do Join ().
Klasa Task dodaje do tego jeden uchwyt, wewnętrzne zdarzenie resetowania ręcznego. Jaki jest najtańszy dostępny zasób systemu operacyjnego. Oczywiście jego metoda Dispose () może zwolnić tylko jeden uchwyt zdarzenia, a nie 5 uchwytów używanych przez Thread. Tak, nie przejmuj się .
Uważaj, że powinieneś być zainteresowany właściwością IsFaulted zadania. To dość brzydki temat, o którym możesz przeczytać więcej w tym artykule w bibliotece MSDN . Kiedy już sobie z tym poradzisz, powinieneś mieć również dobre miejsce w swoim kodzie, aby pozbyć się zadań.
źródło
Thread
w większości przypadków nie tworzy , używa ThreadPool.Chciałbym zobaczyć, jak ktoś rozważa technikę pokazaną w tym poście: Typesafe, uruchom i zapomnij asynchroniczne wywołanie delegata w C #
Wygląda na to, że prosta metoda rozszerzenia poradzi sobie ze wszystkimi trywialnymi przypadkami interakcji z zadaniami i będzie w stanie wywołać metodę dispose na jej temat.
źródło
Task
instancji zwróconej przezContinueWith
, ale zobacz cytat Stephena Touba jest akceptowaną odpowiedzią: nie ma nic do usunięcia, jeśli nic nie wykonuje blokującego oczekiwania na zadanie.Task disper = null; disper = tsk.ContinueWith(cnt => { cnt.Dispose(); disper.Dispose(); });