Strona główna » Algorytmy » Artykuły » Tworzenie Wątków
 

Tworzenie Wątków

Wstęp

Wątki pozwalają w programie na wykonywanie kodu współbieżnie w tym samym procesie. Oznacza to, że możliwe jest wykonanie bardzo czasochłonnych obliczeń, ale bez blokowania głównego okna użytkownika. Ten artykuł ma na celu przedstawienie tworzenia wątków. W kolejnych częściach zostaną opisane problemy oraz jak sobie z nimi radzić.

Wątek

Podczas uruchamiania programu tworzony jest proces, który domyślnie jest jednowątkowy. Oznacza to, że wszystkie operacje są wykonywane jedno po drugim. Utworzenie drugiego wątka powoduje, że obie części programu będą wykonywane współbieżnie. Zazwyczaj pierwszy wątek procesu rozpoczyna nowe wątki i nadzoruje ich cykl życia. Jeden proces może mieć wiele wątków. Wraz z końcem działania procesu wszystkie wątki zostają usunięte.

Praktyczne zastosowanie polega na tym, żeby był główny wątek procesu, który przydziela pracę i tworzy dodatkowe wątki w celu wykonania operacji. Przykładowo jeśli wiadomo, że dana operacja wymaga dużo obliczeń to warto uruchomić tyle wątków ile rdzeni ma procesor, aby wykorzystać całą moc komputera (skuteczność zależy też od systemu operacyjnego i implementacji). Dodatkowy wątek może służyć do odświeżania GUI, aby pokazywać użytkownikowi na bieżąco postępy operacji.

Współbieżność jest uzyskiwana poprzez bardzo szybkie przełącznie wątków na procesorze. Dla użytkownika jest to niemal niezuważalne, ale może to powodować problemy, które zostaną później przedstawione.

Implementacja

Tworzenie wątku

W celu uruchomienia wątku potrzebna jest metoda do wywołania, która będzie jego punktem początkowym wykonywania kodu oraz obiekt klasy Thread. Oto przykładowy kod, który tworzy wątek wykonujący metodę PiszPlus():

  1. ThreadStart metodaA = new ThreadStart(PiszPlus);
  2. Thread watekA = new Thread(metodaA);
  3. watekA.Start();

Z kolei sama metoda PiszPlus() składa się z pętli wypisującej jak wskazuje nazwa metody - plusy:

  1. static void PiszPlus()
  2. {
  3. for(int i = 0; i < 100; i++)
  4. {
  5. Console.Write("+");
  6. }
  7. }

Brakuje jeszcze w głównej metodzie "zamknięcia" wątku. Otóż program zawsze powinien w stosownym momencie zasygnalizować, że ten wątek już nie powinien pracować i wykonywanie dalszej części kodu powinno zostać wstrzymane dopóki tak się nie stanie. Do tego służy metoda Join().

  1. watekA.Join();

Po uruchomieniu programu na konsole zostanie wypisane 50 znaków "+":

  1. +++++++++++++++++++++++++++++++++++++++++++++++++++

Drugi wątek

Tworzymy analogicznie drugi wątek, który z kolei będzie wypisywać minusy. Zostaną one uruchomione równocześnie. Na konsoli sprawdzimy czy są one wykonywane współbieżnie. Uwaga: wynik działania tego programu może się różnić pomiędzy systemem operacyjnym oraz sprzętem komputerowym.

Nowa funkcja główna programu:

  1. ThreadStart metodaA = new ThreadStart(PiszPlus);
  2. Thread watekA = new Thread(metodaA);
  3. watekA.Start();
  4. ThreadStart metodaB = new ThreadStart(PiszMinus);
  5. Thread watekB = new Thread(metodaB);
  6. watekB.Start();
  7. watekA.Join();
  8. watekB.Join();
  9. Console.ReadKey();

Analogiczna metoda do wypisywania minusów:

  1. static void PiszMinus()
  2. {
  3. for (int i = 0; i < 100; i++)
  4. {
  5. Console.Write("-");
  6. }
  7. }

Po uruchomieniu programu na konsoli może pojawić się następujący wynik:

  1. +++++++++++---------------------++++++++++++++++++++++++++++++---------------------++++++++++---------

Na pierwszy rzut oka wynik może wydawać się mylący. Wątki zostały uruchomione współbieżnie, a tutaj działają różnie.. Otóż jak zostało wcześniej wspomniane każdy wątek dostaje pewien kwantyl czas procesora. W tym czasie wątek wykonuje tyle operacji ile da radę i zostaje przełączony na inny. W tym przypadku obserwujemy przełącznie się pomiędzy wypisywaniem plusów, a minusów. W teorii każdy wątek co turę powinien wypisać tyle samo znaków, ale w praktyce na komputerze w tyle działąją inne programy. Ponadto sam program może wykonywać inne operacje:: ładowanie bibliotek czy komunikacja z konsolą. To wszystko wpływa na to, że czas wykonywania pomiędzy wątkami pozwala na mniejszą / większą liczbę operacji.

Podsumowanie

W tym atrykule zostało przedstawione jak tworzyć wątki i uruchomić wybraną przez nas metodę. W następnej części zostanie omówione jak prawidłowo zarządzać cyklem tworzenia i niszczenia wątków w języku C#.

  1. static void Main(string[] args)
  2. {
  3. ThreadStart metodaA = new ThreadStart(PiszPlus);
  4. Thread watekA = new Thread(metodaA);
  5. watekA.Start();
  6. ThreadStart metodaB = new ThreadStart(PiszMinus);
  7. Thread watekB = new Thread(metodaB);
  8. watekB.Start();
  9. watekA.Join();
  10. watekB.Join();
  11. Console.ReadKey();
  12. }

Zadania

Zadanie 1

Napisz program, który uruchomi kilka wątków, które będą wypisywać w pętli numer aktualnego indeksu. Pętla powinna wypisać wartości od 0 do 9 czyli 0123456789. Jaki rezultat można uzyskać uruchumiając tą samą pętle współbieżnie kilka razy?