Laboratorium 2
.Net wątki
To laboratorium ma na celu zapoznanie się z podstawami języka c# wraz z elementami programowania współbieżnego w .net
- 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.)
- pojedynczy pakiet np. mscorelib.dll może zawierać wiele przestrzeni nazw
- każda przestrzeń nazw może zawierać wiele 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
- Pierwszy program (0,1 pkt.)
Uruchomić Visual Studio
Utworzyć nowy projekt konsolowy o nazwie Watki, nazwa solucji może być np. lab1 czy DotNet1
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). - 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. - Wątki z lambdą (0,5 pkt.)
Uruchomić 10 wątków anonimowo przekazując w parametrze liczbę iteracji. Wykorzystać notację lambda. - 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?
- 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)
- 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ć.
- Kończenie wątków (0,9 pkt.).
- Do bieżącej solucji dodać nowy projekt konsolowy o nazwie Ending. Uwaga projekt ma być typu .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.
- 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
- Sprawozdanie
Nie zapomnijcie o sprawozdaniu