.Net: Wątki w C# cz.2

Wątki

W pierwszej części pisałem o podstawach wątków. Były tam poruszane takie tematy jak tworzenie wątków, ich blokowanie, a także współdzielenie stanu. Dzisiaj postaram się pogłębić ten temat i zwrócić uwagę na bezpieczeństwo wątków.

Stan współdzielony vs. stan lokalny

W ostatnim przykładzie części pierwszej pisałem o stanie współdzielonym wątków. Wrzuciłem tam taki przykład:

Jak w łatwy sposób zmienić powyższy kod, by stan ze współdzielonego stał się lokalnym? Najpierw zastanówmy się dlaczego wątki współdzielą stan. Jest to efekt działania dwóch elementów. Po pierwsze zarówno metody które chcemy wywołać jak i zmienna stanowiąca o ich stanie są składowymi statycznymi klasy WorldOfThreads. Po drugie wszystkie są wywoływane jako składowe tej samej klasy (co zresztą implikuje powód pierwszy). Musimy ten stan rzeczy odwrócić. Najpierw należy usunąć słowo kluczowe static z każdego miejsca oprócz nagłówka metody Main. Następnie metody PrintCubes i PrintSquares należy podawać do wątków, jako składowe osobnych instancji klasy WorldOfThreads. Ostatni krok to zrobienie czegoś z pętlą w metodzie Main. Możemy albo nadpisać pole from  w metodzie Main, albo wynieść całą pętlę do osobnej metody i wywołać ją z nowego obiektu WorldOfThreads. Zdecydowałem się na to drugie rozwiązanie – zobaczmy kod po tych transformacjach:

Teraz wszystko jest w porządku, tzn. nie możemy zdeterminować która metoda pierwsza zakończy klasę, ale mamy pewność że każda z nich zrobi swoje od początku do końca 🙂

Co w przypadku gdy nie jesteśmy w stanie uniknąć stanu współdzielonego?

W powyższym przykładzie dosyć łatwo mogliśmy sprawić by wątki przestały współdzielić stan. W większości sytuacji jesteśmy wstanie tak napisać rozwiązanie by stan był lokalny. Nie mniej mogą się zdarzyć przypadki gdy jest to niemożliwe, albo przynajmniej bardzo trudne (np. w danym miejscu korzystamy z Singletonu). W takich przypadkach możemy skorzystać z mechanizmu tzw. blokad. Blokady to dosyć szeroki i nie najłatwiejszy temat, dlatego poświęcę mu kiedyś osobny wpis, a póki co tylko wspomnę o nim by pamiętać że coś takiego istnieje.  Jest to technika pozwalająca na koordynowanie działania wątków w taki sposób, by przy stanie współdzielonym otrzymać deterministyczny wynik.

Rozróżniamy dwa rodzaje blokad. Blokady wykluczające i blokady bez wykluczenia. Różnica polega na tym, że blokady wykluczające działają w taki sposób że, na kodzie/danych które obejmują  pozwalają pracować tylko jednemu wątkowi. Korzystając z blokad bez wykluczenia możemy określić liczbę wątków którym chcemy pozwolić pracować na danym kodzie.

Więcej o tym napiszę za jakiś czas, nie mniej na koniec przykład, przedstawiający jak mniej więcej wygląda zakładanie blokad. W roli głównej podstawowy konstrukt jeżeli chodzi o blokady wykluczające – lock:

Jak możemy się domyślić, zamysłem autora klasy ImportantThingsCalculator było jednorazowe wypisanie efektu “ważnych obliczeń” na ekranie. Niestety autor tego API nie przewidział, że ktoś może użyć go w sposób jaki widać na drugim listingu. Metoda PrintCalculation zostaje wywołana 3 razy – mamy też 3 wątki. Po wykonaniu powyższego, na wyjściu widzimy śmieci w stylu:

Podczas gdy oczekiwane wyjście to:

Z pomocą przychodzi nam konstrukcja lock:

Przy tej wersji klasy ImportantThingsCalculator dostaniemy spodziewany output. Zobaczmy co się zmieniło. Po pierwsze jako składowa pojawił się obiekt, który nazwałem blocker. Jak widać nie ma w nim żadnej magii zobaczmy więc jego zastosowanie. Całe ciało metody PrintCalculation, zostało umieszczone w bloku lock(blocker). Obiekt synchronizacji (w tym przypadku nasz blocker) istnieje po to by wątek mógł na nim założyć blokadę. Na raz tylko jeden wątek może blokować dany obiekt, więc pozostałe wątki które próbują to robić muszą czekać na zwolnienie blokady (czyli de facto zakończenia pracy wątku).  Nie mniej na ten temat napiszę więcej we wpisie poświęconym blokadom wykluczającym.


To był dosyć długi wpis poświęcony praktycznie w całości problemom związanym ze stanem współdzielonym wątków i zaletom płynącym ze zmiany stanu na lokalny. Jak już wspominałem blokady doczekają się ode mnie osobnego wpisu(albo dwóch ;)) natomiast w kolejnej części dotyczącej wątków napiszę o priorytetach wątków, oraz o podstawach tzw. sygnalizowania.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *