Często odpowiadam na wiele pytań związanych z wielowątkowością i często widzę to samo podstawowe pytanie zadawane na różne sposoby. Przedstawię najczęstsze problemy, jakie widziałem na przestrzeni lat i wyjaśnię, w jaki sposób nowsze technologie ułatwiły rozwiązanie tych problemów.
zamykania nad zmiennej sterującej
nie jest problemem specyficzne przewlekanie, ale użycie gwintowanie zdecydowanie powiększa problem. C# 5.0 rozwiązuje ten problem dla pętli foreach
, tworząc nową zmienną dla każdej iteracji. Nie będziesz już musiał tworzyć specjalnej zmiennej dla zamknięć wyrazów lambda. Niestety, pętla for
nadal będzie wymagać obsługi specjalną zmienną przechwytywania.
czekając na zadania asynchroniczne wypełnić
.NET 4.0 wprowadzono klasę CountdownEvent
który hermetyzuje wiele logiki wymagane czekać na zakończenie wielu zadań. Większość młodszych programistów używała połączeń Thread.Join
lub pojedynczego połączenia WaitHandle.WaitAll
. Oba mają problemy z skalowalnością. Stary wzorzec miał używać pojedynczego ManualResetEvent
i sygnalizować, gdy licznik osiągnie zero. Licznik został zaktualizowany przy użyciu klasy Interlocked
. CountdownEvent
czyni ten wzór znacznie łatwiejszym. Pamiętaj tylko, aby traktować swoją pracę główną jako pracownika, aby uniknąć subtelnych warunków wyścigu, które mogą wystąpić, gdy jeden pracownik zakończy pracę, zanim wszyscy pracownicy zostaną w kolejce.
. NET 4.0 wprowadził także klasę Task
, która może mieć zadania potomne powiązane z nią przez TaskCreationOptions.AttachedToParent
. Jeśli zadzwonisz pod numer Task.Wait
na rodzica, to będzie czekać na zakończenie wszystkich zadań podrzędnych.
Producent-Konsument
.NET 4.0 wprowadzono klasę BlockingCollection
który działa jak normalny kolejce wyjątkiem, że można go zablokować, gdy zbiór jest pusty. Można kolejkować obiekt, wywołując Add
i usuwając kolejkę obiektów, wywołując Take
. Take
bloków, dopóki element nie będzie dostępny. To znacznie upraszcza logikę producenta-konsumenta. Kiedyś programiści próbowali napisać własną klasę blokowania kolejek. Ale jeśli nie wiesz, co robisz, możesz naprawdę to zepsuć ... źle. W rzeczywistości przez najdłuższy czas Microsoft miał przykład blokującej kolejki w dokumentacji MSDN, która sama została poważnie uszkodzona. Na szczęście od tego czasu został usunięty.
Aktualizacja UI z postępu wątku pracownik
Wprowadzenie BackgroundWorker
wykonane wydzielenia zadanie tła z aplikacji WinForm dużo łatwiejsze dla początkujących programistów. Główną zaletą jest to, że można zadzwonić pod numer ReportProgress
z poziomu obsługi zdarzeń DoWork
, a programy obsługi zdarzeń ProgressChanged
zostaną automatycznie przełożone na wątek interfejsu użytkownika. Oczywiście każdy, kto śledzi moje odpowiedzi na SO, wie, co myślę o operacjach marszałkowych (poprzez Invoke
lub tym podobnych) jako rozwiązanie do aktualizacji interfejsu użytkownika z prostymi informacjami o postępach. Rozrywam go cały czas, ponieważ jest to generalnie okropne podejście. BackgroundWorker
nadal zmusza programistę do modelu push (poprzez operacje marszałkowskie w tle), ale przynajmniej robi to wszystko za kulisami.
niewytworność z Invoke
Wszyscy wiemy, że element UI można uzyskać tylko z wątku UI. Oznaczało to ogólnie, że programista musiał użyć operacji marszałkowskich poprzez ISynchronizeInvoke
, DispatcherObject
lub , aby przenieść kontrolę z powrotem do wątku interfejsu użytkownika. Ale spójrzmy prawdzie w oczy. Te operacje marshalingowe wyglądają brzydko. Task.ContinueWith
uczyniło to nieco bardziej eleganckim, ale prawdziwa chwała przechodzi na await
jako część nowego asynchronicznego modelu programowania C# 5. await
można użyć do czekania, aż Task
zostanie zakończone w taki sposób, że kontrola przepływu zostanie tymczasowo przerwana podczas wykonywania zadania, a następnie zwrócona w tym miejscu w odpowiednim kontekście synchronizacji. Nie ma nic bardziej eleganckiego i satysfakcjonującego niż użycie await
jako zamiennika dla wszystkich tych połączeń Invoke
.
programowanie równoległe
często widzę pytania pytając, jak rzeczy mogą się zdarzyć równolegle. Starym sposobem było stworzenie kilku wątków lub użycie ThreadPool
. .NET 4.0 użył TPL i PLINQ. Klasa Parallel
to świetny sposób na równoległe wykonywanie iteracji pętli. I PLINQ's AsParallel
to inna strona tej samej monety dla zwykłego starego LINQ. Te nowe funkcje TPL znacznie upraszczają tę kategorię programowania wielowątkowego.
.NET 4.5 wprowadza bibliotekę Przepływ danych TPL. Ma to na celu stworzenie eleganckiego złożonego problemu programowania równoległego. Abstrahuje klasy do bloków. Mogą to być bloki docelowe lub bloki źródłowe. Dane mogą płynąć z jednego bloku do drugiego. Istnieje wiele różnych bloków, w tym BufferBlock<T>
, BroadcastBlock<T>
, ActionBlock<T>
itd., Które wszystkie robią różne rzeczy. I oczywiście cała biblioteka zostanie zoptymalizowana do użycia z nowymi słowami kluczowymi async
i await
. Jest to ekscytujący nowy zestaw zajęć, który moim zdaniem będzie się powoli przyzwyczajał.
zakończenie Płynnego
jak masz wątku, aby zatrzymać? Często widzę to pytanie. Najprostszym sposobem jest zadzwonić pod numer Thread.Abort
, ale wszyscy wiemy, jak to zrobić ... Mam nadzieję. Istnieje wiele różnych sposobów, aby to zrobić bezpiecznie. .NET 4.0 wprowadził bardziej zunifikowaną koncepcję zwaną anulowaniem za pośrednictwem CancellationToken
i CancellationTokenSource
. Zadania w tle mogą sondować IsCancellationRequested
lub po prostu dzwonić pod ThrowIfCancellationRequested
w bezpiecznych punktach, aby z wdzięcznością przerwać pracę, którą wykonali. Inne wątki mogą zadzwonić pod numer Cancel
, aby poprosić o anulowanie.
Delegaci i wydarzenia nie mają nic wspólnego z wątkami, a nie są specyficzne dla .NET 4.5. Proszę wyjaśnić, ponieważ nie mogę powiedzieć, o czym mówisz. –
@JohnSaunders Async Delegaci ułatwiają nawiązywanie wątków; zobacz http://msdn.microsoft.com/en-us/library/22t547yb.aspx – LamonteCristo
Czy zauważyłeś, że asynchroniczne korzystanie z delegatów było w .NET od wersji 1.0? –