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



Podobne dokumenty
using System;... using System.Threading;

Rozdział 3. Zapisywanie stanu aplikacji w ustawieniach lokalnych

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

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

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Instrukcja laboratoryjna nr.4

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

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

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

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

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

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

Informatyka II. Laboratorium Aplikacja okienkowa

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.

Co to jest NODE.JS? Nowoczesne środowisko programistyczne

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

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

Klasy i obiekty cz II

Po uruchomieniu programu nasza litera zostanie wyświetlona na ekranie

Programowanie zaawansowane

Programowanie obiektowe

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

Podstawy i języki programowania

Programowanie obiektowe

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

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

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

Wprowadzenie do projektu QualitySpy

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

Wyrażenie include(sciezka_do_pliku) pozwala na załadowanie (wnętrza) pliku do skryptu php. Plik ten może zawierać wszystko, co może się znaleźć w

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

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

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Rozdział 4 KLASY, OBIEKTY, METODY

TEMAT : KLASY DZIEDZICZENIE

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

Wstęp do programowania 2

Programowanie obiektowe

Programowanie obiektowe

Wykład 8: klasy cz. 4

Delphi Laboratorium 3

Instrukcja laboratoryjna cz.3

Dziedziczenie. Tomasz Borzyszkowski

Czym są właściwości. Poprawne projektowanie klas

Programowanie obiektowe

Klasy cd. Struktury Interfejsy Wyjątki

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

Wyjątki (exceptions)

Temat: Programowanie zdarzeniowe. Zdarzenia: delegacje, wykorzystywanie zdarze. Elementy Windows Application (WPF Windows Presentation Foundation).

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Podstawy programowania skrót z wykładów:

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

5. Model komunikujących się procesów, komunikaty

Informatyka I: Instrukcja 4.2

Smarty PHP. Leksykon kieszonkowy

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli.

Programowanie obiektowe - 1.

Programowanie w środowiskach graficznych. Wykład 3 Język C#

Wskaźniki do funkcji i metod

Dokumentacja do API Javy.

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Języki C i C++ Wykład: 2. Wstęp Instrukcje sterujące. dr Artur Bartoszewski - Języki C i C++, sem. 1I- WYKŁAD

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

Programowanie Obiektowe Ćwiczenie 4

Metody Metody, parametry, zwracanie wartości

Mechanizmy pracy równoległej. Jarosław Kuchta

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

Wstęp do programowania obiektowego. Wykład 2

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

Podstawy programowania w języku C++

Podstawy i języki programowania

Multimedia JAVA. Historia

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

3 Delegacje. 3.1 Tworzenie delegacji. 3.2 Skojarzenie delegacji z procedurą czy funkcją

PARADYGMATY PROGRAMOWANIA Wykład 4

Testowanie II. Celem zajęć jest zapoznanie studentów z oceną jakości testów przy wykorzystaniu metryk pokrycia kodu testami (ang. code coverage).

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

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

ALGORYTMY I STRUKTURY DANYCH

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Zasady programowania Dokumentacja

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

Technologie obiektowe

Algorytmy i złożoności. Wykład 3. Listy jednokierunkowe

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

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

Sesje, ciasteczka, wyjątki. Ciasteczka w PHP. Zastosowanie cookies. Sprawdzanie obecności ciasteczka

Programowanie obiektowe

Podstawy Programowania 2

Programowanie strukturalne i obiektowe. Funkcje

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

TECHNOLOGIE INTERNETOWE WYKŁAD 6. JavaScript Funkcje i obiekty

Zapis algorytmów: schematy blokowe i pseudokod 1

Komunikacja za pomocą potoków. Tomasz Borzyszkowski

Zaawansowane techniki programowania C#

Podstawy programowania. Wykład PASCAL. Zmienne wskaźnikowe i dynamiczne. dr Artur Bartoszewski - Podstawy prograowania, sem.

Diagramy klas. dr Jarosław Skaruz

PHP 5 język obiektowy

Transkrypt:

Programowanie asynchroniczne. Operator await i modyfikator async Język C# 5.0 wyposażony został w nowy operator await, ułatwiający synchronizację dodatkowych zadań uruchomionych przez użytkownika. Poniżej zaprezentuję prosty przykład jego użycia, który chyba wyjaśni jego działanie. Działnie tego operatora związane jest ściśle z biblioteką TPL (od ang. Task Parallel Library) i jej sztandarową klasą Task, które zostaną omówione w kolejnych rozdziałach. Jednak podobnie jak w przypadku opisanej niżej pętli równoległej Parallel.For, tak i w przypadku operatora await, dogłębna znajomość biblioteki TPL nie jest konieczna. Spójrzmy na przykład widoczny na listingu 1.1, który przedstawia metodę zdarzeniową przycisku. Zdefiniowana jest w niej przykładową akcja pobierająca obiekt typu object a zwracająca liczbę całkowitą long. Referencję do niej zapisuję w zmiennej akcja i uruchamiam ją (synchronicznie). Czynność owa wprowadza jednosekundowe opóźnienie metodą Thread.Sleep (należy zadeklarować użycie przestrzeni nazw System.Threading 1 ), które oczywiście opóźnia wykonywanie całej metody zdarzeniowej po kliknięciu przycisku. W efekcie na jedną sekundę aplikacja zamiera. Listing 1.1. Synchroniczne wykonywanie kodu zawartego w akcji private void button1_click(object sender, EventArgs e) Func<object, long> akcja = (object argument) => ; msgbox("akcja: Początek, argument: " + argument.tostring()); Thread.Sleep(1000); //opóźnienie msgbox("akcja: Koniec"); return DateTime.Now.Ticks; msgbox("wynik: "+akcja("synchronicznie")); void msgbox(string komunikat) string taskid = Task.CurrentId.HasValue? Task.CurrentId.ToString() : "UI"; MessageBox.Show("! " + komunikat + " (" + taskid + ")"); W metodzie przedstawionej na listingu 1.2 ta sama akcja wykonywana jest asynchronicznie w osobnym wątku utworzonym przez platformę.net na potrzeby zdefiniowanego przez nas zadania (instancja klasy Task z TPL). Synchronizacja następuje w momencie odczytania wartości zadanie.result, czyli tak naprawdę wartości zwracanej przez czynność akcja. Jej sekcja get czeka ze zwróceniem wartości aż do zakończenia akcji wykonywanej przez zadanie wstrzymując do tego czasu wątek, w którym wykonywana jest metoda button1_click. Jest to zatem typowy punkt synchronizacji, choć trochę ukryty. Zwróćmy uwagę, że po 1 Alternatywnie moglibyśmy użyć instrukcji await Task.Delay(1000);, ale wówczas musielibyśmy oznaczyć wyrażenie lambda jako async, a wówczas należałoby referencję do niego zapisać w zmiennej typu Func<object, Task<long>>.

instrukcji zadanie.start(), a przed odczytaniem własności zadanie.result mogą być wykonywane dowolne czynności, o ile są niezależne od wartości zwróconej przez zadanie. Listing 1.2. Użycie zadania do asynchronicznego wykonania kodu private void button1_click(object sender, EventArgs e) Func<object, long> akcja = (object argument) => ; msgbox("akcja: Początek, argument: " + argument.tostring()); Thread.Sleep(1000); //opóźnienie msgbox("akcja: Koniec"); return DateTime.Now.Ticks; Task<long> zadanie = new Task<long>(akcja, "zadanie"); zadanie.start(); msgbox("akcja została uruchomiona"); if (zadanie.status!= TaskStatus.Running && zadanie.status!=taskstatus.rantocompletion) msgbox("zadanie nie zostało uruchomione"); else msgbox("wynik: "+zadanie.result); Nie jest konieczne, aby instrukcja odczytania własności Result znajdowała się w tej samej metodzie, co uruchomienie zadania należy tylko do miejsca jej odczytania przekazać referencję do zadania (w naszym przypadku zmienną typu Task<long>). Zwykle referencję tę przekazuje się jako wartość zwracaną przez metodę uruchamiającą zadanie. Przykład takiej metody widoczny jest na listingu 1.3. Jeżeli używamy angielskich nazw metod, jest zwyczajem, aby metoda tworząca i uruchamiająca zadanie miały przyrostek..async. Listing 1.3. Wzór metody wykonującej jakąś czynność asynchronicznie Task<long> DoSomethingAsync(object argument) Func<object, long> akcja = (object _argument) => ; msgbox("akcja: Początek, argument: " + _argument.tostring()); Thread.Sleep(1000); //opóźnienie msgbox("akcja: Koniec"); return DateTime.Now.Ticks; Task<long> zadanie = new Task<long>(akcja, argument); zadanie.start(); return zadanie; protected void button1_click(object sender, EventArgs e)

Task<long> zadanie = DoSomethingAsync("zadanie-metoda"); msgbox("akcja została uruchomiona"); if (zadanie.status!= TaskStatus.Running && zadanie.status!=taskstatus.rantocompletion) msgbox("zadanie nie zostało uruchomione"); else msgbox("wynik: " + zadanie.result); Po tym wprowadzeniu możemy przejść do omówienia zasadniczego tematu. Wraz z wersjami 4.0 i 4.5 w platformie.net (oraz w platformie Windows Runtime) pojawiło się wiele metod podobnych do przedstawionej powyżej metody DoSomethingAsync (ale oczywiście w odróżnieniu od niej robiących coś pożytecznego). Metody te wykonują asynchronicznie różnego typu długotrwałe czynności. Znajdziemy je w klasie HttpClient, w klasach odpowiedzialnych za obsługę plików (StorageFile, StremWriter, StreamReader, XmlReader), w klasach odpowiedzialnych za kodowanie i dekodowanie obrazów, czy w klasach WCF. Asynchroniczność jest wręcz standardem w aplikacjach Windows 8 z interfejsem Modern UI. I właśnie aby ich użycie było (prawie) tak proste, jak metod synchronicznych wprowadzony został w C# 5.0 (co odpowiada platformie.net 4.5) operator await. Ułatwia on synchronizację dodatkowego zadania tworzonego przez te metody. Należy jednak pamiętać, że metodę, w której chcemy użyć operatora await musimy oznaczyć modyfikatorem async. Prezentuje to listing 1.4. Listing 1.4. Przykład użycia modyfikatora async i modyfikatora await protected async void button1_click(object sender, EventArgs e) Task<long> zadanie = DoSomethingAsync("async/await"); msgbox("akcja została uruchomiona"); long wynik = await zadanie; msgbox("wynik: " + wynik); Operator await zwraca parametr użyty w klasie parametrycznej Task<>. Zatem w przypadku zadania typu Task<long> będzie to zmienna typu long. Jeżeli użyta została wersje nieparametryczna klasy Task, operator zwraca void i służy jedynie do synchronizacji (nie przekazuje wyniku; nieparametryczna klasa Task nie ma także własności Result). Metody oznaczone modyfikatorem async nazywane są w angielskiej dokumentacji MSDN async method. Może to jednak wprowadzać pewne zamieszanie. Z powodu tej nazwy metody z modyfikatorem async (w naszym przypadku metoda Button1_Click) utożsamiane są bowiem z metodami wykonującymi asynchronicznie jakieś czynności (a taką w naszym przypadku jest DoSomethingAsync). Osobom poznającym dopiero temat często wydaje się, że aby metoda wykonywana była asynchronicznie wystarczy dodać do jej sygnatury modyfikator async. To nie jest prawda! Możemy wywołać metodę DoSomethingAsync w taki sposób, że umieścimy ją bezpośrednio za operatorem await np. long wynik = await DoSomethingAsync("async/await");. Jaki to ma sens? Wykonywanie metody button1_click, w której znajduje się to wywołanie zostanie wstrzymane aż do momentu zakończenia metody DoSomethingAsync, więc efekt jaki zobaczymy na ekranie będzie identyczny, jak w przypadku synchronicznym (listing 1.1). Różnica jest jednak zasadnicza, i to jest zasadnicza nowość, bowiem instrukcja zawierająca operator await nie blokuje wątku, w którym wywołana została metoda button1_click. Kompilator zawiesza wywołanie metody button1_click przechodząc do kolejnych czynności w miejscu jej wywołania aż do momentu zakończenia uruchomionego zadania. W momencie, gdy to nastąpi, wątek wraca do

metody button1_click i kontynuuje jej działanie 2. W programie, na którym w tej chwili testujemy operator await, efektów tego jednak nie zobaczymy. Efekt będzie widoczny dopiero, gdy metodę button1_click wywołamy z innej metody niech będzie to metoda zdarzeniowa button2_click związana z drugim przyciskiem. Zauważmy, że w serii instrukcji wywołanie metody oznaczonej modyfikatorem async nie musi się zakończyć przed wykonaniem następnej instrukcji i w tym sensie jest ona asynchroniczna. Aby tak się stało musi w niej jednak zadziałać operator await czekający na wykonanie jakiegoś zadania (w naszym przykładzie metody DoSomethingAsync). W efekcie, w scenariuszu przedstawionym na listingu 1.5 metoda button2_click zakończy się przed zakończeniem button1_click. Listing 1.5. Działanie modyfikatora async private async void button1_click(object sender, EventArgs e) long wynik = await DoSomethingAsync("async/await"); msgbox("wynik: " + wynik.tostring()); private void button2_click(object sender, EventArgs e) button1_click(null, null); Ważna rzecz: samo użycie operatora await i modyfikatora async nie powoduje utworzenia nowych zadań lub wątków! Powoduje jedynie przekazanie na pewien czas sterowania z metody, w której znajduje się operator await i oznaczonej modyfikatorem async do metody, która ją wywołała i powrót w momencie ukończenia zadania, na które czeka await. Koszt jest zatem niewielki i rozwiązanie to może być z powodzeniem stosowane bez obawy o utratę wydajności. Ponadto, właśnie z uwagi na wydajność, operator await sprawdza czy w momencie, w którym dociera do niego sterowanie, metoda asynchroniczna nie jest już zakończona. Jeżeli tak, praca kontynuowana jest synchronicznie bez zbędnych skoków. Metoda z modyfikatorem async może zwracać wartość void tak, jak w przypadku przedstawionej wyżej metody zdarzeniowej button1_click. Jednak w takim przypadku jej działanie nie może być żaden sposób synchronizowane. Po uruchomieniu nie mamy nad nią żadnej kontroli. W szczególności nie można użyć operatora await, ani metody Wait klasy Task, aby poczekać na jej zakończenie. Aby to było możliwe metoda z modyfikatorem async musi zwracać referencję Task lub Task<>. Wówczas możliwe jest użycie operatora await, za którym można zresztą ustawić dowolne wyrażenie o wartości Task lub Task<> (zmienne i własności tego typu oraz metody lub wyrażenia lambda zwracające wartość tego typu 3 ). Przekazane zadanie umożliwia synchronizację. Ponadto użycie wersji parametrycznej umożliwia zwrócenie wartości przekazywanej potem przez operator await. Sprawdźmy to tworząc odpowiednik metody button1_click ze zmienioną sygnaturą (nie możemy tego zrobić z oryginałem, bo jest związany ze zdarzeniem button1.click). Nowa metoda o nazwie 2 Aby taki efekt uzyskać bez operatora await, należałoby użyć konstrukcji opartej na funkcjach zwrotnych (ang. callback). W efekcie kod stałby się raczej skomplikowany i przez to podatny na błędy. Warto też zauważyć, że await nie jest prostym odpowiednikiem metody Task.Wait, która po prostu zatrzymałaby bieżący wątek do momentu zakończenia zadania. W przypadku operaotra await nastąpi przekazanie sterowania do metody wywołującej i powrót w momencie zakończenia zadania. 3 Gwoli prawy, należałoby to stwierdzenie uściślić, bowiem nie tylko zadania mogą być argumentem operatora await, a każdy typ, który zwraca metodę GetAwaiter. Więcej informacji dostępnych jest na stronie FAQ zespołu odpowiedzialnego za implementację mechanizmu async/await w platformie.net (http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx).

DoSomethingMoreAsync widoczna jest na listingu 1.6 4. Usunąłem argumenty, których i tak nie używaliśmy i zmieniłem zwracaną wartość z void na Task. Dzięki temu metoda ta nie jest już typu wystrzel i zapomnij, a może być kontrolowana z miejsca uruchomienia (zob. widoczna również na listingu 1.6 metoda button2_click). Zdziwienie może budzić jednak fakt, że za słowem kluczowym return w metodzie DoSomethingMoreAsync wcale nie ma instrukcji tworzącej zwracane przez tą metodę zadanie (tak naprawdę instrukcji return mogłoby wcale nie być). W metodach z modyfikatorem async i zwracających wartość Task, zadanie jest przypisywane przez kompilator. W ten sposób ułatwiona jest wielostopniowa obsługa metod asynchronicznych. Należy jednak pamiętać, że te metody nie tworzą nowych zadań, a jedynie je przekazują. Listing 1.6. Metoda async zwracająca zadanie private async Task DoSomethingMoreAsync() msgbox("dosomethingmoreasync: Początek"); long wynik = await DoSomethingAsync("async/await"); msgbox("dosomethingmoreasync: Wynik: " + wynik.tostring()); msgbox("dosomethingmoreasync: Koniec"); return; private async void button2_click(object sender, EventArgs e) await DoSomethingMoreAsync(); A co w przypadku metod async, które miałyby zwracać wartość. Załóżmy, że metoda DoSomethingMore miałaby zwracać wartość typu long (np. wartość zmiennej wynik). W takim przypadku należy zmienić typ tej metody na Task<long>, a za słowem kluczowym return wstawić wartość typu long. Pokazuje to listing 1.7. Warto zapamiętać, choć to uproszczone stwierdzenie, że w metodach async operator await wyłuskuje z typu Task<> parametr, a słowo kluczowe return w metodach async zwracające wartość typu Task<> działa odwrotnie otacza dowolne obiekty typem Task<>. Listing 1.7. Metoda async zwracająca wartość long private async Task<long> DoSomethingMoreAsync() msgbox("dosomethingmoreasync: Początek"); long wynik = await DoSomethingAsync("async/await"); msgbox("dosomethingmoreasync: Wynik: " + wynik.tostring()); msgbox("dosomethingmoreasync: Koniec"); return wynik; private async void button2_click(object sender, EventArgs e) msgbox("button2_click: Wynik: " + await DoSomethingMoreAsync()); 4 Zwróćmy uwagę na przyrostek Async. W końcu jest to teraz metoda, która działa asynchronicznie, choć żadnego zadania nie tworzy.

I kolejna sprawa. Co w metodach async dzieje się w przypadku błędów? Nieobsłużone wyjątki zgłoszone w metodzie z modyfikatorem async i zwracające zadania (Task lub Task<>) są za pośrednictwem tych zadań przekazywane do metody wywołującej. Można zatem użyć normalnej konstrukcji try..catch, jak na listingu 1.8. Gorzej jest w przypadku metod async zwracających void (tych typu wystrzel i zapomnij, jak button1_click z naszego przykładu). Wówczas wyjątek przekazywany jest do puli wątków kryjącej się za mechanizmem zadań i przechwytywanie wyjątków nic nie da. Listing 1.8. Obsługa wyjątków zgłaszanych przez metody async private async void button2_click(object sender, EventArgs e) try msgbox("button2_click: Wynik: " + await DoSomethingMoreAsync()); catch(exception exc) msgbox("button2_click: Błąd!\n" + exc.message);