Mam stylistyczne pytanie dotyczące wyboru implementacji wątku w tle, którego powinienem użyć w aplikacji formularza Windows. Obecnie mam BackgroundWorker
na formularzu, który ma nieskończoną (while(true))
pętlę. W tej pętli używam WaitHandle.WaitAny
drzemki wątku, dopóki nie wydarzy się coś interesującego. Jeden z uchwytów zdarzenia, na które czekam, to „ StopThread
” zdarzenie, dzięki czemu mogę wyrwać się z pętli. To zdarzenie jest sygnalizowane, gdy z mojego zastąpienia Form.Dispose()
.
Czytałem gdzieś, które BackgroundWorker
jest naprawdę przeznaczone do operacji, z którymi nie chcesz wiązać interfejsu użytkownika i które mają skończony koniec - jak pobieranie pliku lub przetwarzanie sekwencji elementów. W tym przypadku „koniec” jest nieznany i tylko wtedy, gdy okno jest zamknięte. Czy zatem bardziej odpowiednie byłoby dla mnie użycie wątku w tle zamiast BackgroundWorker
do tego celu?
źródło
CancelAsync
(i sprawdzaj,CancellationPending
czy twój wątek będzie odpytywany w krótkich odstępach czasu, jeśli zamiast tego chcesz mieć zgłoszony wyjątek, użyj a,System.Threading.Thread.Abort()
który wywołuje wyjątek w samym bloku wątku, wybierz model odpowiedni do sytuacji.Niektóre z moich myśli ...
źródło
BackgroundWorker
ma na celu raportowanie postępu wątku zainteresowanej stronie, co zwykle obejmuje interfejs użytkownika. Dokumentacja MSDN dla tej klasy wyjaśnia to bardzo jasno. Jeśli po prostu potrzebujesz zadania do wykonania w tle, lepiej użyjThreadPool
wątku.System.Windows.Forms
zgromadzenia;BackgroundWorker
jest również przydatny w przypadku aplikacji WPF i te aplikacje mogą nie mieć odniesienia do WinForms.Prawie to, co powiedział Matt Davis, z następującymi dodatkowymi punktami:
Dla mnie głównym wyróżnikiem
BackgroundWorker
jest automatyczne kierowanie zakończonego zdarzenia za pośrednictwemSynchronizationContext
. W kontekście interfejsu użytkownika oznacza to, że zakończone zdarzenie jest uruchamiane w wątku interfejsu użytkownika, a zatem może służyć do aktualizowania interfejsu użytkownika. Jest to główny wyróżnik, jeśli używaszBackgroundWorker
w kontekście interfejsu użytkownika.Zadania wykonywane za pośrednictwem programu
ThreadPool
nie mogą być łatwo anulowane (obejmuje toThreadPool
.QueueUserWorkItem
I delegaci wykonują asynchronicznie). Tak więc, podczas gdy unika się narzutu związanego z rozpędzaniem wątku, jeśli potrzebujesz anulowania, użyjBackgroundWorker
lub (bardziej prawdopodobne poza interfejsem użytkownika) , podkręć wątek i zachowaj do niego odniesienie, aby móc wywołaćAbort()
.źródło
Ponadto wiążesz wątek puli wątków na czas życia pracownika w tle, co może budzić niepokój, ponieważ jest ich tylko skończona liczba. Powiedziałbym, że jeśli kiedykolwiek tworzysz wątek tylko raz dla swojej aplikacji (i nie używasz żadnej z funkcji programu roboczego w tle), użyj wątku zamiast wątku pracującego w tle / puli wątków.
źródło
Wiesz, czasami po prostu łatwiej jest pracować z BackgroundWorkerem, niezależnie od tego, czy używasz Windows Forms, WPF czy jakiejkolwiek innej technologii. Fajną częścią tych facetów jest to, że masz wątki bez martwienia się zbytnio o to, gdzie jest wykonywany wątek, co jest świetne w przypadku prostych zadań.
Przed użyciem
BackgroundWorker
rozważ najpierw, jeśli chcesz anulować wątek (zamknięcie aplikacji, anulowanie użytkownika), musisz zdecydować, czy Twój wątek powinien sprawdzać odwołania, czy też powinien być narzucony na samo wykonanie.BackgroundWorker.CancelAsync()
zostanie ustawionyCancellationPending
na,true
ale nie zrobi nic więcej, wtedy wątki są odpowiedzialne za ciągłe sprawdzanie tego, pamiętaj również, że w tym podejściu możesz skończyć z sytuacją wyścigu, w której użytkownik anulował, ale wątek został ukończony przed testowaniemCancellationPending
.Thread.Abort()
z drugiej strony zgłosi wyjątek w wykonaniu wątku, który wymusza anulowanie tego wątku, ale musisz uważać na to, co może być niebezpieczne, jeśli ten wyjątek został nagle zgłoszony podczas wykonywania.Gwintowanie wymaga bardzo dokładnego rozważenia bez względu na zadanie, do dalszego czytania:
Programowanie równoległe w najlepszych rozwiązaniach dotyczących zarządzania wątkami .NET Framework
źródło
Wiedziałem, jak używać wątków, zanim poznałem .NET, więc zanim zacząłem używać
BackgroundWorker
s. Matt Davis podsumował różnicę z wielką doskonałością, ale dodałbym, że trudniej jest zrozumieć, co dokładnie robi kod, a to może utrudnić debugowanie. Łatwiej jest myśleć o tworzeniu i zamykaniu wątków, IMO, niż o przydzielaniu pracy puli wątków.Nadal nie mogę komentować postów innych osób, więc wybacz mi chwilową kulawiznę w używaniu odpowiedzi na adres piers7
Nie używaj
Thread.Abort();
zamiast tego, sygnalizuj zdarzenie i zaprojektuj swój wątek tak, aby kończył się wdzięcznie, gdy zostanie zasygnalizowany.Thread.Abort()
podnosi wartośćThreadAbortException
w dowolnym momencie wykonywania wątku, co może powodować różne nieszczęśliwe rzeczy, takie jak osierocone monitory, uszkodzony stan współdzielenia i tak dalej.http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx
źródło
Jeśli się nie zepsuł - napraw, aż będzie ... tylko żartuję :)
Ale poważnie, BackgroundWorker jest prawdopodobnie bardzo podobny do tego, co już masz, gdybyś zaczął z nim od początku, może zaoszczędziłbyś trochę czasu - ale w tym momencie nie widzę takiej potrzeby. Chyba że coś nie działa lub myślisz, że twój obecny kod jest trudny do zrozumienia, wtedy trzymałbym się tego, co masz.
źródło
Podstawowa różnica polega na tym, że, jak powiedziałeś, generowanie zdarzeń GUI z pliku
BackgroundWorker
. Jeśli wątek nie musi aktualizować wyświetlacza ani generować zdarzeń dla głównego wątku GUI, może to być prosty wątek.źródło
Chcę zwrócić uwagę na jedno zachowanie klasy BackgroundWorker, o którym jeszcze nie wspomniano. Możesz sprawić, aby normalny wątek działał w tle, ustawiając właściwość Thread.IsBackground.
Możesz przetestować to zachowanie, wywołując następującą metodę w konstruktorze okna formularza.
Gdy właściwość IsBackground jest ustawiona na true i zamkniesz okno, aplikacja zostanie normalnie zakończona.
Ale gdy właściwość IsBackground jest ustawiona na false (domyślnie) i zamkniesz okno, tylko okno zniknie, ale proces będzie nadal działał.
Klasa BackgroundWorker wykorzystuje Thread, który działa w tle.
źródło
Pracujący w tle to klasa, która działa w oddzielnym wątku, ale zapewnia dodatkowe funkcje, których nie można uzyskać za pomocą prostego wątku (np. Obsługa raportów o postępach zadań).
Jeśli nie potrzebujesz dodatkowych funkcji zapewnianych przez pracownika w tle - a wydaje się, że nie - wtedy bardziej odpowiedni byłby wątek.
źródło
Wprawia mnie w zakłopotanie fakt, że projektant studia wizualnego pozwala tylko na używanie BackgroundWorkerów i Timerów, które w rzeczywistości nie działają z projektem usługi.
Zapewnia zgrabną kontrolę przeciągania i upuszczania do usługi, ale ... nawet nie próbuj jej wdrażać. Nie zadziała.
Usługi: używaj tylko System.Timers.Timer System.Windows.Forms.Timer nie będzie działać, mimo że jest dostępny w zestawie narzędzi
Usługi: BackgroundWorkers nie będzie działać, gdy działa jako usługa. Zamiast tego użyj System.Threading.ThreadPools lub wywołań Async
źródło