Laboratorium 5

by Paweł Paduch published 2018/12/03 11:10:00 GMT+2, last modified 2023-11-03T08:54:44+02:00
.Net wątki

To laboratorium ma na celu zapoznanie się z podstawami języka c# wraz z elementami programowania współbieżnego w .net

  1. Wstęp
    W 2000 r. Microsoft przedstawił pomysł standaryzacji oprogramowania na platformę Windows. Główne cechy .net:
    • nowe biblioteki
    • nowe narzędzia
    • niezależność językowa (VB,C#,C++,J#, i dziesiątki innych)
    • niezależność platformowa dzięki CIL - Common Intermediate Language
    • implementacje na Linuxa np. mono czy dotGNU
    • w pełni obiektowy
    • "odśmiecaniem" pamięci zajmuje się środowisko uruchomieniowe
    • zaawansowane mechanizmy programistyczne (delegaty, refleksje, typy generyczne, lambdy, zapytania LINQ itp.)
    Różnice pomiędzy pakietem a przestrzenią nazw:
    • pojedynczy pakiet np. mscorelib.dll może zawierać wiele przestrzeni nazw
    • każda przestrzeń nazw może zawierać wiele typów
    Przestrzeń nazw to zgrupowanie semantyczne powiązanych typów znajdujących się w pakiecie np. system.IO, System.Data, System.Thread. Ułatwia to zrozumienie i logiczne zorganizowanie typów.
    Kilka wybranych przestrzeni nazw:
    • System - wiele przydatnych typów związanych z danymi wewnętrznymi, zmiennymi środowiskowymi i odśmiecaniem, jak również wiele powszechnie stosowanych wyjątków i atrybutów.
    • System.Collections - Definicja wielu typów kontenerowych, jak również bazowe typy i interfejsy, pozwalające budować niestandardowe kolekcje.
    • System.Data, System.data - Komunikacja z relacyjnymi bazami danych za pomocą ADO.NET
    • System.IO - Wiele typów używanych w plikowych operacjach we/wy, kompresji danych i manipulowaniu portami.
    • System.Reflection - Typy obsługujące wykrywanie typów w czasie wykonywania programu, jak również dynamiczne tworzenie typów.
    • System.Drawing, System.Windows.Forms - Typy używane do budowania aplikacji okienkowych za pomocą oryginalnego zestawu narzędzi interfejsu użytkownika .Net (Windows Forms)
    • System.ServiceModel - Służy do budowania aplikacji rozproszonych z apomocą API WCF (Windows Communication Foundation)
    • System.Web - Służy do budowania aplikacji internetowych ASP.NET
    • System.Threading - Liczne typy do budowania aplikacji wielowątkowych
    • System.Xml - Przestrzenie nazw związane z językiem XML
    Do typów znajdujących się w przestrzeni nazw można odwoływać się za pomocą pełnej nazwy np.: System.Threading.Thread lub użyć using System.Threading i wołać bezpośrednio Thread.

  2. Pierwszy program (0,1 pkt.)
    Uruchomić Visual Studio
    Utworzyć nowy projekt konsolowy o nazwie Watki, nazwa solucji może być np. lab1 czy DotNet1 
    new_project1.png
    Przepisać poniższy kod do pliku Program.cs:
    class Program
    {
        static int liczbaIteracji = 10;
        static int liczbaWatkow = 10;
        
        static void wyswietlajCos()
        {
            Console.WriteLine(Thread.CurrentThread.Name + " zaczyna wyświetlać liczby:");
            Random rand = new Random();
            for (int ii = 0; ii < liczbaIteracji; ii++)
            {
              Console.Write("{0}, ", ii); //wyświetl liczbę
              Thread.Sleep(rand.Next(0, 2000)); //wstrzymaj działanie na czas od 0 do 2s
            }
            Console.WriteLine(Thread.CurrentThread.Name + " zakończył pisać liczby.");
        }
    
        static void Main(string[] args)
        {
            Thread[] tablicaWatkow = new Thread[liczbaWatkow]; 
            for (int licznik = 0; licznik < liczbaWatkow; licznik++) //tworzymy, nadajemy nazwę i uruchamiamy wątki
            {
                tablicaWatkow[licznik] = new Thread(new ThreadStart(wyswietlajCos));
                tablicaWatkow[licznik].Name = "Wątek nr " + licznik.ToString();
                tablicaWatkow[licznik].Start();
            }
            foreach (Thread watek in tablicaWatkow) //czekamy na wszystkie wątki
            {
                watek.Join();
            }
        Console.WriteLine("To już jest koniec, naciśnij ENTER...");
        Console.ReadLine();
        }
    }
    Skompilować i uruchomić F5 (tryb debug) lub ctrl+F5 (tryb release).

  3. Przekazanie parametru do funkcji wątka (0,5 pkt.) 
    W powyższym programie stworzono 10 wątków wykonujących 10 iteracji pętli. Liczba iteracji jest obecnie zadeklarowana na sztywno jako zmienna globalna.  Stwórz nową funkcję wyswietlajCosPar, której będzie można przekazać przez parametr liczbę iteracji jaka ma być przez nią wykonana. Użyj jej do tworzenia 10 wątków. 

  4. Wątki z lambdą (0,5 pkt.)  
    Uruchomić 10 wątków anonimowo przekazując w parametrze liczbę iteracji. Wykorzystać notację lambda.

  5. Priorytetowość (1.5 pkt) 
    • Stworzyć dwa wątki (watekH i watekL) a w nich po 16 wątków, w których wykonamy instrukcję  Thread.SpinWait(1000000000); (przy słabszych komputerach można tę wartość zmniejszyć, przy szybszych można jej użyć kilkukrotnie).
    • W wątkach typu H ustawiamy przed startem własność Priority = ThreadPriority.Highest
    • W wątkach typu L Priority = ThreadPriority.Lowest.
    • Wystartować oba wątki.
    • Za pomocą Stopwatch zmierzyć ile milisekund potrzeba na wykonanie wątków typu H a ile typu L. (wątki H i L powinny działać jednocześnie). Które wątki wykonały się szybciej?
    • Gdy program działa stabilnie (możemy doczekać się jego końca) można na samym początku ustawić priorytetowość procesu Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
    • Uruchomić i zaobserwować co się dzieje?

  6. Wątki i lock (1 pkt.)
    • Do istniejącej solucji dodać nowy projekt Synch. (wybrać z menu kontekstowego solucji Add->New Project)
    • Ustawić nowy projekt jako domyślny do uruchomienia (z menu kontekstowego projektu wybrać Set as StartUp Project)
       SetAsStartupProject.png
    • Wykorzystując kod z pierwszego projektu, utwórz 10 wątków wykonujących poniższą metodę.
      static void wyswietlajCos()
      {
      Random rand = new Random(Thread.CurrentThread.ManagedThreadId);//inicjuj random numerem wątka
      Thread.Sleep(rand.Next(0, 100));
      Console.WriteLine(Thread.CurrentThread.Name + " zaczyna pisać:");

      for (int ii = 0;ii<liczbaIteracji;ii++)
      {
      Console.Write("ala ");
      Thread.Sleep(rand.Next(0,100)); //wstrzymaj działanie na czas od 0 do 0,1
      Console.Write("ma ");
      Thread.Sleep(rand.Next(0,100)); //wstrzymaj działanie na czas od 0 do 0,1
      Console.WriteLine("kota " + ii);
      Thread.Sleep(rand.Next(0,2000)); //wstrzymaj działanie na czas od 0 do 2s
      }
      Console.WriteLine(Thread.CurrentThread.Name + " zakończył pisać.");
      }
    • Skompilować i zobaczyć jak działa dla kilku wątków
    • Zastosować lock tak, by każdy napis "ala ma kota" był wyświetlany w całości, jednak należy to zrobić w ten sposób, by bez usuwania sleepów program działał w miarę najszybciej jak się da. 
    • Zwrócić uwagę, że początkowe napisy i końcowe też mogę przepleść wyświetlanie zadania Ala ma kota. To też należy zabezpieczyć. 
  7. Kończenie wątków (0,9 pkt.)
    • Do bieżącej solucji dodać nowy projekt konsolowy o nazwie Ending. Uwaga projekt ma mieć .net framework nie .net core.
    • Zastosować poniższy kod do utworzenia i wystartowania wątku:
                  Thread watek = new Thread(() =>
                  {
                      Console.WriteLine("Wątek uruchomiony i idzie spać...");
                      try
                      {
                          Thread.Sleep(2000);
                      }
                      catch (ThreadInterruptedException tiex)
                      {
                          Console.WriteLine("Złapałem wyjątek ThreadInterruptedException: " + tiex.Message);
                      }
                      catch (ThreadAbortException taex)
                      {
                          Console.WriteLine("Złapałem wyjątek ThreadAbortException: " + taex.Message);
                      }
                      finally
                      {
                          Console.WriteLine("Wykonuję finally");
                      }
                      Console.WriteLine("Wątek kończy działanie normalnie");
                  });
                  watek.Start();
      
    • W głównym wątku dodać czekanie na wątek potomny a przed tym przerwać wątek za pomocą Interrupt. Zaobserwować co się dzieje na konsoli. 
    • Zamiast Interrupt zastosować Abort. Zaobserwować co się dzieje na konsoli. 
    • Teraz przed Abort dodać 1 sekundowego sleepa i znowu zaobserwować wyniki. 
    • W bloku łapiącym ThreadAbortException zastosować ResetAbort i zaobserwować różnice.

  8. Stany wątku (0,5 pkt.)
    Do powyższego programu dodać wypisywanie stanu wątku tak by uzyskać  poniższe stany, sprawdzić dla Abort i Interrupt:
    • Unstarted
    • Unstarted, AbortRequested
    • Running
    • WaitSleepJoin
    • AbortRequested
    • Aborted
    • Stopped

  9. Sprawozdanie

        Nie zapomnijcie o sprawozdaniu