using System;... using System.Threading;

Podobne dokumenty
msgbox("akcja: Początek, argument: " + argument.tostring()); Thread.Sleep(1000); //opóźnienie msgbox("akcja: Koniec"); return DateTime.Now.

Rozdział 3. Zapisywanie stanu aplikacji w ustawieniach lokalnych

Instrukcja laboratoryjna cz.3

- Narzędzie Windows Forms. - Przykładowe aplikacje. Wyższa Metody Szkoła programowania Techniczno Ekonomiczna 1 w Świdnicy

Programowanie telefonów z Windows Phone 7, cz. 2

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Instrukcja laboratoryjna nr.4

Kurs programowania 2 - listy

Przykładowa dostępna aplikacja w Visual Studio - krok po kroku

xmlns:prism= c. <ContentControl prism:regionmanager.regionname="mainregion" />

Prosta książka telefoniczna z wykorzystaniem zapisu do pliku

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Programowanie obiektowe

Programowanie obiektowe i zdarzeniowe

Java. Wykład. Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Projektowanie Graficznych Interfejsów Użytkownika Robert Szmurło

Programowanie obiektowe i zdarzeniowe wykład 1 Wprowadzenie do programowania zdarzeniowego

Laboratorium 10 - Web Services

Leszek Stasiak Zastosowanie technologii LINQ w

1 Wątki 1. 2 Tworzenie wątków 1. 3 Synchronizacja 3. 4 Dodatki 3. 5 Algorytmy sortowania 4

Programowanie obiektowe

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

Przewodnik krok po kroku:

Wątki w Javie. Piotr Tokarski

Aplikacje w Javie- wykład 11 Wątki-podstawy

Współbieżność w środowisku Java

Programowanie zaawansowane

Programowanie w JAVA Lab. 5 - Wątki. 1. Wykorzystując metodę Monte Carlo narysować wykres funkcji oraz obliczyć całkę: 7 x ) xy, 8,8

Marcin Luckner Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych

Programowanie obiektowe

Programowanie obiektowe

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Programowanie telefonów z Windows Phone 7, cz. 4

Co to jest NODE.JS? Nowoczesne środowisko programistyczne

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Programowanie wielowątkowe. Tomasz Borzyszkowski

Wykład 8: klasy cz. 4

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

Wskaźniki do funkcji i metod

Wprowadzenie do projektu QualitySpy

Wykład 4 Delegat (delegate), właściwości indeksowane, zdarzenie (event) Zofia Kruczkiewicz

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

4 C#. Tworzenie aplikacji sieciowych. 101 gotowych projektów

Multimedia JAVA. Historia

Politechnika Poznańska Wydział Budowy Maszyn i Zarządzania

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

STWORZENIE MOBILNEJ APLIKACJI,

Język JAVA podstawy. wykład 2, część 2. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

5.2. Pierwsze kroki z bazami danych

C# 6.0 : kompletny przewodnik dla praktyków / Mark Michaelis, Eric Lippert. Gliwice, cop Spis treści

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Utworzenie aplikacji mobilnej Po uruchomieniu Visual Studio pokazuje się ekran powitalny. Po lewej stronie odnośniki do otworzenia lub stworzenia

Wielowątkowość. Programowanie w środowisku rozproszonym. Wykład 1.

Metody Metody, parametry, zwracanie wartości

Systemy mobilne. Laboratorium. Dostęp do danych GPS w Windows Phone 7

LINQ TO XML. Autor ćwiczenia: Marcin Wolicki

Prosta aplikacja klient - serwer na bazie protokoªu UDP. Sprawozdanie.

Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04

Implementacja aplikacji sieciowych z wykorzystaniem środowiska Qt

01 grid tablica grid. Copyright 2017, mgr inż. Janusz Bonarowski 1

akademia androida Service, BroadcastReceiver, ContentProvider część IV

Klasy cd. Struktury Interfejsy Wyjątki

Zaawansowane aplikacje WWW - laboratorium

Programowanie w technologii.net wykład 4 Aplikacja i okna

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Informatyka II. Laboratorium Aplikacja okienkowa

Swing Application Framework czyli tam i z powrotem. Copyright Piotr Kochański & Erudis,

Architektury Usług Internetowych. Laboratorium 2. Usługi sieciowe

Pętle. Dodał Administrator niedziela, 14 marzec :27

Wykład 3 Składnia języka C# (cz. 2)

KUP KSIĄŻKĘ NA: PRZYKŁADOWY ROZDZIAŁ KOMUNIKATY DLA UŻYTKOWNIKA

Ćwiczenie 2. Obsługa gniazd w C#. Budowa aplikacji typu klient-serwer z wykorzystaniem UDP.

Stworzenie klasy nie jest równoznaczne z wykorzystaniem wielowątkowości. Uzyskuje się ją dopiero poprzez inicjalizację wątku.

MVVM Light Toolkit. Julita Borkowska

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

dr Artur Bartoszewski dr Artur Bartoszewski - Aplikacje mobilne - Wykład

Klasy i obiekty cz II

Kompleksowe tworzenie aplikacji klasy Desktop z wykorzystaniem SWT i

Zaawansowane programowanie w C++ (PCP)

Politechnika Poznańska Wydział Budowy Maszyn i Zarządzania

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Kurs programowania. Wykład 8. Wojciech Macyna. 10 maj 2017

PROBLEMY TECHNICZNE. Co zrobić, gdy natrafię na problemy związane z użytkowaniem programu DYSONANS

Platformy Programistyczne Zagadnienia sieciowe i wątki

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

Laboratorium programowania urządzeń mobilnych

Spis treści. Rozdział 1. Aplikacje konsoli w stylu ANSI C i podstawowe operacje w Visual C

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

1. Wprowadzenie do WPF i XAML. Tworzenie interfejsu użytkownika.

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Aplikacje WWW - laboratorium

Kurs WWW. Paweł Rajba.

Wielowątkowość mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Programowanie obiektowe

10. Programowanie obiektowe w PHP5

Rozdział 2. Pierwsza aplikacja Windows Store.

Aktywności są związane z ekranem i definiują jego wygląd. Dzieje się to poprzez podpięcie do aktywności odpowiedniego widoku.

Transkrypt:

Kontekst synchronizacji Wątek w platformie.net może posiadać kontekst synchronizacji reprezentowany przez instancję klasy SynchronizationContext lub jej klasy potomnej. Jeżeli wątek posiada taki kontekst (co można sprawdzić odczytując statyczną własność SynchronizationContext.Current), możliwe jest przekazanie referencji do niego do innego wątku. Wówczas za pomocą metod tego obiektu Send lub Post możliwe jest uruchomienie wskazanej w ich argumencie metody lub wyrażenia Lambda w wątku, z którym kontekst synchronizacji jest związany. Brzmi to może dość zawile, ale w istocie nie jest bardziej skomplikowane niż użycie metod Control.InvokeRequired i Control.Invoke. Pokazuje to przykład widoczny na listingu 5.A. Jest to kod z pliku Form1.cs aplikacji Windows Forms, w której na formie umieszczony jest pasek postępu (progressbar1) i przycisk (button1). W aplikacji tego typu kontekst synchronizacji jest automatycznie tworzony dla wątków odpowiedzialnych za obsługę okien (wątków interfejsu). Jest to obiekt typu WindowsFormsSynchronizationContext, zdefiniowany w przestrzeni System.Windows.Forms, ale potomny względem klasy SynchronizationContext z przestrzeni nazw System.Threading. Listing 5.A. Użycie kontekstu synchronizacji w aplikacji Windows Forms using System; using System.Threading; namespace KontekstSynchronizacji_WinForm public partial class Form1 : Form static int min = 0; static int max = 100; static int opoznienie = 100; public Form1() InitializeComponent(); resetujinterfejs(null, null); private void resetujinterfejs(object sender, EventArgs e) progressbar1.minimum = min; progressbar1.maximum = max; progressbar1.value = min; private void ustawwartoscpaskapostepu(object parametr) int nowawartosc = (int)parametr; progressbar1.value = nowawartosc;

SynchronizationContext kontekst = parametr as SynchronizationContext; kontekst.send(ustawwartoscpaskapostepu, i); kontekst.send((object niewykorzystany)=> button3.text = "Koniec";, null); private void button1_click(object sender, EventArgs e) SynchronizationContext kontekst = SynchronizationContext.Current; Thread t = new Thread(kontrolaPaskaPostepu); t.start(kontekst); Interesująca nas część przebiegu aplikacji rozpoczyna się wraz z kliknięciem przycisku, które uruchamia metodę zdarzeniową button1_click. Metoda ta wykonywana jest rzecz jasna w wątku obsługującym interfejs. W tej metodzie tworzony jest wątek, który ma wykonać metodę kontrolapaskapostepu. Do tej metody przesyłany jest kontekst synchronizacji wątku interfejsu, czyli instancja klasy WindowsFormsSynchronizationContext odczytana ze statycznej własności SynchronizationContext.Current. Działająca w dodatkowym wątku metoda kontrolapaskapostepu wykonuje pętlę for z indeksem przebiegającym wartości od 0 do 100. W kolejnych iteracjach pętli zmieniana jest własność Value paska postępu. Ponieważ pętla ta nie jest wykonywana w wątku, w którym powstała kontrolka paska postępu, nie mogą bezpośrednio odwoływać się jej własności (progressbar1.value = i;). Groziłoby to pojawieniem się wyjątku InvalidOperationException. Tu z pomocą przychodzi kontekst synchronizacji, którego metodą Send uruchomiamy metodę ustawwartoscpaskapostepu. Metoda ta wykonywana będzie w wątku, z którego ów kontekst pochodzi, a więc w wątku interfejsu. W tej ostatniej metodzie można zatem bez obaw odczytywać i modyfikować kontrolki umieszczone na formie. Definiowanie osobnej metody ustawwartoscpaskapostepu nie jest wcale konieczne. Równie dobrze możliwe byłoby wykorzystanie wyrażenia Lambda, którego sygnatura zgodna jest z delegatem SendOrPostCallback a więc przyjmuje jeden argument typu object i nie zwraca wartości: kontekst.send((object nowawartosc) => progressbar1.value = (int)nowawartosc;, i);. W tym przykładzie zależało mi jednak na wyraźnym odseparowaniu tej części kodu, która choć wywoływana w dodatkowym wątku, wykonywana będzie w wątku wskazanym przez kontekst synchronizacji. Dla porównania zestawmy powyższy przykład z listingiem 5.B, na którym widoczny jest analogiczny kod, ale korzystający z metod Control.InvokeRequired i Control.Invoke. Zwróćmy uwagę na podobieństwa obu podejść. W obu przypadkach wątek dodatkowy musi mieć referencję do obiektu związanego z wątkiem interfejsu. Jest to obiekt kontekstu synchronizacji w pierwszym przypadku, a referencja do dowolnej kontrolki w drugim. Teoretycznie rzecz biorąc mechanizm oparty na kontekście synchronizacji mógłby być użyty dla dowolnych dwóch wątków, z których żaden nie musi być wątkiem okna. Można w ten sposób na przykład rozdzielić dwa moduły aplikacji z dwoma niezależnymi wątkami, zachowując ich pełną niezależność względem siebie. W praktyce samodzielne utworzenie kontekstu synchronizacji nie jest jednak łatwe zwykle wykorzystywane są tylko gotowe konteksty dostarczone wraz z bibliotekami kontrolek, a więc WindowsFormsSynchronizationContext w przypadku Windows Forms, DispatcherSynchronizationContext w przypadku WPF i AspNetSynchronizationContext w przypadku ASP.NET. Konteksty synchronizacji dostarczone wraz z platformą.net pozwalają na wyraźne rozdzielenie warstwy interfejsu (widoku) ze zdefiniowanymi w niej synchronicznymi metodami modyfikującymi interfejs i warstwy logiki, która nie musi zawierać żadnych bezpośrednich odniesień do interfejsu i działających w nim kontrolek. Wystarczy jej wiedza o kontekście synchronizacji wątku interfejsu. Zaletą użycia kontekstu

synchronizacji jest prostota kodu, który z niego korzysta. Wystarczy porównać metody ustawwartoscpaskapostepu z listingu 5.A i 5.B 1. Listing 5.B. Przykład analogiczny do listingu 5.A, ale synchronizacja przeprowadzana za pomocą Control.Invoke using System; using System.Threading; namespace Invoke_WinForm public partial class Form1 : Form static int min = 0; static int max = 100; static int opoznienie = 100; public static void ustawwartoscpaskapostepu(progressbar progressbar, int nowawartosc) if (progressbar.invokerequired) progressbar.invoke( new new Action<ProgressBar,int>(ustawWartoscPaskaPostepu), new object[] progressbar, nowawartosc ); else if(progressbar!=null) progressbar.value = nowawartosc; ustawwartoscpaskapostepu_invoke(progressbar1, i); button1.invoke(new Action(() => button1.text = "Koniec"; )); 1 Po więcej informacji warto zajrzeć na strony: http://msdn.microsoft.com/en-us/magazine/gg598924.aspx, http://www.codeproject.com/articles/31971/understanding-synchronizationcontext-part-i.

private void button1_click(object sender, EventArgs e) Thread t = new Thread(kontrolaPaskaPostepu_Invoke); t.start(checkbox1.checked); Warto też zaznaczyć, że obok metod blokujących SynchronizationContext.Send i Control.Invoke, w obu mechanizmach mamy także do dyspozycji metody wykonywane asynchronicznie: SynchronizationContext.Post i Control.BeginInvoke (oraz Control.EndInvoke). One również uruchamiają zadania w wątku interfejsu, ale nie blokują wątku dodatkowego. W naszym przykładzie użycie metody Post nie ma jednak większego sensu. Spowodowałoby to szybkie przebiegnięcie pętli for (pomijając sztucznie wprowadzone do niej opóźnienie) i tym samym zakolejkowanie stu zadań do wykonania w wątku interfejsu, które byłyby wykonywane po kolei. Metody Post i Send różnią się także sposobem obsługi błędów. W przypadku metody Send wyjątki zgłaszane w metodzie wykonywanej w wątku okna (w naszym przykładzie metoda ustawwartoscpaskapostepu) są przekazywane do miejsca wywołania metody Send. Dzięki temu ma sens otoczenie wywołania tej metody konstrukcją try..catch. To bardzo wygodne rozwiązanie, które niestety nie zadziała w przypadku asynchronicznego wykonywania czynności zainicjowanych przez metodę Post. Wówczas wyjątki skazane są na brak obsłużenia i w efekcie najprawdopodobniej przerwą działanie wątku okna. Miłą zaletą mechanizmu synchronizacji wątku interfejsu opartego na kontekście synchronizacji jest to, że w zasadzie bez żadnych zmian można kod z listingu 5.A zastosować także w projektach WPF. Odtwórzmy w aplikacji WPF projekt aplikacji z paskiem postępu i przyciskiem. Przykładowy kod XAML widoczny jest na listingu 5.C. Listing 5.C. Kod określający wygląd przykładowej aplikacji WPF <Window x:class="kontekstsynchronizacji_wpf.mainwindow" <Grid> xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="117.335" Width="540.885"> <ProgressBar Name="progressBar1" HorizontalAlignment="Left" Height="29" Margin="10,10,0,0" VerticalAlignment="Top" Width="498"/> <Button Name="button1" Content="Kontekst synchronizacji" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" Width="234" RenderTransformOrigin="0.104,-0.768" Height="28" Click="Button_Click_1"/> </Grid> </Window> W metodzie zdarzeniowej przycisku Button_Click_1 stwórzmy dodatkowy wątek, który będzie wykonywał metodę kontrolapaskapostepu. Wygląd tych dwóch metod nie różni się niczym zasadniczym od ich wersji z aplikacji Windows Forms poza faktem, że kontekst synchronizacji jest teraz instancją klasy System.Windows.Threading.DispatcherSynchronizationContext. Tak samo, jak w aplikacji Windows Forms wygląda również metoda ustawwartoscpaskapostepu wywoływana w kontekście wątku interfejsu. Wszystkie trzy metody widoczne są na listingu 5.D. Listing 5.D. Użycie kontekstu synchronizacji w aplikacji WPF private void ustawwartoscpaskapostepu(object parametr) int nowawartosc = (int)parametr; progressbar1.value = nowawartosc; Komentarz [JM1]: A jak jest z obsługą błędów w Invoke i BeginInvoke?

SynchronizationContext kontekst = parametr as SynchronizationContext; kontekst.send(ustawwartoscpaskapostepu, i); kontekst.send((object niewykorzystany) => button1.content = "Koniec";, null); private void Button_Click_1(object sender, RoutedEventArgs e) SynchronizationContext kontekst = SynchronizationContext.Current; Thread t = new Thread(kontrolaPaskaPostepu); t.start(kontekst); Nieco różnic jest w przypadku aplikacji dla Windows 8 z interfejsem Modern UI. Po pierwsze mamy tam do czynienia z platformą WinRT, a nie zwykłą platformą.net. Platforma ta nie wspiera tworzenia wątków ani za pomocą klasy Thread, ani za pomocą puli wątków. Możemy jednak odtworzyć aplikację używając zadań (zob. rozdział 6.). Wówczas przekonamy się, że z wątkiem interfejsu związany jest kontekst synchronizacji zaimplementowany w klasie WinRTSynchronizationContext z przestrzeni nazw System.Threading. Sposób jego przekazania i użycia jest w zasadzie podobny, jak w powyższych przykładach z tą istotną różnicą, że nie ma wsparcia dla metody Send, a jedynie dla asynchronicznej metody Post. Groźba zagłodzenia wątku interfejsu W przypadku aplikacji z interfejsem graficznym, w których dodatkowe wątki modyfikują wygląd interfejsu, należy pamiętać o wszystkich niebezpieczeństwach, jakie czyhają na nas w przypadku aplikacji wielowątkowych. W szczególności bardzo łatwo o zakleszczenie, w efekcie którego może dojść do zagłodzenia wątku interfejsu. Aby to pokazać wystarczy na końcu metody zdarzeniowej uruchamianej po kliknięciu przycisku z listingów 5.A, 5.B bądź 5.D umieścić wywołanie metody Join na rzecz tworzonych w nich wątków (instrukcja t.join()). Efektem będzie trwałe zastygnięcie aplikacji tuż po kliknięciu przycisku. Dlaczego? Użycie metody Control.Invoke lub metody SynchronizeContext.Send, a więc metod, które wstrzymują bieżący wątek do czasu, gdy zakończy się wykonywanie działań w wątku interfejsu, należy traktować jak punkt synchronizacji. Prześledźmy działanie obu wątków na przykładzie kodu z listingu 5.D. Po kliknięciu przycisku przez użytkownika, w wątku interfejsu uruchamiana jest metoda Button_Click_1. W niej tworzony jest dodatkowy wątek, w którym zaczyna działać pętla for. W jej pierwszej iteracji wywoływana jest metoda kontekst.send, która stara się zakolejkować czynność do wykonania przez wątek interfejsu i czeka na jej zakończenie. Ale wątek interfejsu nadal wykonuje metodę Button_Click_1 nie mogąc jej opuścić ze względu na polecenie t.join, które z kolei czeka na zakończenie dodatkowego wątku. A skoro tak, nie może wykonać czynności zleconych w metodzie Send. W konsekwencji oba wątki stoją nawzajem na siebie czekając. Zatrzymanie wątku interfejsu powoduje, że interfejs aplikacji nie odpowiada na czynności użytkownika. Zadanie 1. Przygiotuj wrapper do kontrolki ProgressBar, w którym dostęp do własności jest bezpieczny ze względu na wątki.