петак, 27. мај 2016.

Asinhrono, async i await višenitno programiranje

Dolaskom C# programskog jezika verzije 5, u C# programski jezik su uključene dve nove ključne reči async i await, dve komande koje ne idu jedna bez druge. Laički rečeno, tamo gde vidite da je metoda definisana sa async, u toj metodi trebate imati i ključnu reč await. Njihova magija je u tome što one omogućavaju višenitno programiranje ali bez direktnog formiranja niti. Sve do pojave asinhronog programiranja, svi vaši .Net programi bez dodatnog programiranja su sve operacije izvršavale sekvencijalno. To u prevodu znači da su se operacije izvršavale jedna po jedna i svaka operacija je morala čekati da se svaka prethodna operacija izvrši do kraja. Kad god napravite aplikaciju poput Windows Form, ona poseduje samo jednu nit; Main thread – glavnu ili UI nit; i sad zamislite da u toj niti koristite I/O operaciju. Vaš Windows će tada čekati dok vi pristupate nekom fajlu na hard disku ili mreži dok god se vaša operacija ne izvrši do kraja. Zbog toga Windows pauzira vašu nit tako da on ne koristi ni jedan CPU resurs ali na taj način zadržava memoriju.


( Asinhronizovano programiranje )

Asinhrono programiranje je jednostavno rešilo ovaj problem, jer asinhrono programiranje za razliku od sekvencijalnog ili sinhronog programiranja vam omogućava da vi pokrenete neku metodu i nastavljate da radite neki drugi posao dok se metoda ne završi. Inače sa sinhronim programiranjem pokrećete neku metodu; morate da čekate da se metoda izvrši do kraja; prosledi vam se neki rezultat i onda se nastavlja vaše izvršavanje programa. Danas se u programiranju koristi i jedna i druga varijanta u zavisnosti da li radite sa I/O operacijama ili jednostavno vam je potrebno više vremena da se izvrši neka metoda. Danas celi koncept Funkcionalnog programiranja je daleko bolji kad se radi preko asinhronizovani delegata, dok na primer UWP ili Phone aplikacija bez asinhroni metoda je nezamisliva jer svaka metoda koja radi duže od 2 sekunde je neprihvatljiva. Asinhrono programiranje se koristi kad imate kompleksne upite koje uzimaju dosta vremena, šaljete email-ove ili radite sa fajlovima. Nadam se da vam je sad jasno koliko je asinhrono programiranje bitno, ali takođe treba da znate da asinhronizovano programiranje nije lako jer se vi morate pobrinuti da ništa ne krene pogrešno, takođe je komplikovano debugovanje i održavanje istog. Ključnu reč async koristite da markirate metodu za asinhronizovanu operaciju. Na taj način vi samo dajete signal vašem kompajleru da će se nešto asinhronizovano desiti. Kompajler tada transformiše vašu kod u programersku C# paradigmu koju zovemo state machine. Znači pokreće sinhronično trenutnu nit, ali vam omogućava da vašu metodu razdvojite u više delova. Kad koristite await ključnu reč, kompajler će generisati kod koji će te videti bez obzira da li je vaša asinhronizovana operacija završena. Ako se vaša metoda samo sinhronizovano nastavlja, u prevodu nije završena; onda će state machine spojiti sa kontinuiranom metodom koja će se pokrenuti kada se task završi. Prepustiće kontrolu pozivajućoj niti i ta nit će se moći koristiti za drugi posao. Imajte u vidu da asinhronizovana metoda može da vrati rezultat samo kao void, Task i Task<T>.

Kako izgleda neki praktični primer asinhronog programiranja?



Pre najjednostavnijeg praktičnog primera asinhronog programiranja, treba da znate ukoliko kod ključne reči async u nekoj markiranoj asinhroničnoj metodi u Microsoft Visual Studio-u izostavite ključnu reč await, velike su šanse da će te dobiti grešku ili u najmanju ruku samo informaciju da treba da koristite ključnu reč await iako u principu asinhrona metoda u takvom slučaju se izvršava sinhronizovano. Takođe, ne možete markirati ključnom rečju glavnu metodu Main(). Sledeći primer jednostavno skida sav html tekst sa neke web stranice; u ovom slučaju početnu stranicu mog bloga i smešta je u string promenjivu, zatim je prikazuje u Command Prompt konzoli. Nemojte mnogo da se opterećujete ukoliko u kodu vidite neku klasu ili nešto što nikad niste pre učili, nego se jednostavno navikavajte da će te u naprednom programiranju često viđati kod u kojem su vam neke stvari nove. I ne očekujte odmah detaljno objašnjenje i da odmah sve znate. Već se kasnije i malo sami pokrenite i istražujte. U sledećem primeru će te videti kako se koristi klasa HttpClient; i to ne znači da sad odmah morate da znate Internet programiranje da bi ste shvatili primer. U ovom primeru je poenta kako da markirate i pozovete asinhronizovanu metodu. Za task sam mogao staviti bilo šta što zahteva više vremena da se task izvrši.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace AsyncAndAwait
{
    class Program
    {
        static void Main(string[] args)
        {
            string result = DownloadContent().Result;
            WriteLine(result);

            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }

        public static async Task<string> DownloadContent()
        {
            using (HttpClient client = new HttpClient())
            {
                string result = await client.GetStringAsync("http://www.manuelradovanovic.com");
                return result;

            }
        }
    }
}

Kad pokrenete navedeni kod, rezultat će biti isti ukoliko koristite domen mog bloga za primer. U vašem vežbanju možete staviti bilo koji link. Skidanje generisanog koda u tekst formatu je veoma korisna stvar prilikom učenja HTML - HyperText Markup Language-a. Ovde je prikazan samo deo rezultata.

. . .

displayModeFull'));

_WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', null, document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'viewType': 'FILTERED_POSTMOD', 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/2422706338-lbx__sr.js', 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/368954415-lightbox_bundle.css'}, 'displayModeFull'));
_WidgetManager._RegisterWidget('_TranslateView', new _WidgetInfo('Translate1', 'sidebar-right-1', null, document.getElementById('Translate1'), {}, 'displayModeFull'));
_WidgetManager._RegisterWidget('_ProfileView', new _WidgetInfo('Profile1', 'sidebar-right-1', null, document.getElementById('Profile1'), {}, 'displayModeFull'));
_WidgetManager._RegisterWidget('_ImageView', new _WidgetInfo('Image1', 'sidebar-right-1', null, document.getElementById('Image1'), {'resize': true}, 'displayModeFull'));
_WidgetManager._RegisterWidget('_PollView', new _WidgetInfo('Poll1', 'sidebar-right-1', null, document.getElementById('Poll1'), {'pollid': '4434971421144870211', 'iframeurl': 'https://www.google.com/reviews/polls/display/4434971421144870211/blogger_template/run_app?txtclr\x3d%23444444\x26lnkclr\x3d%230058cd\x26chrtclr\x3d%230058cd\x26font\x3dnormal+normal+16px+Arial,+Tahoma,+Helvetica,+FreeSans,+sans-serif\x26hideq\x3dtrue\x26purl\x3dhttp://www.manuelradovanovic.com/'}, 'displayModeFull'));
_WidgetManager._RegisterWidget('_PopularPostsView', new _WidgetInfo('PopularPosts1', 'footer-2-1', null, document.getElementById('PopularPosts1'), {}, 'displayModeFull'));
_WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML1', 'footer-2-2', null, document.getElementById('HTML1'), {}, 'displayModeFull'));
_WidgetManager._RegisterWidget('_AttributionView', new _WidgetInfo('Attribution1', 'footer-3', null, document.getElementById('Attribution1'), {'attribution': '&#169;2015 Manuel Radovanovic Lincenca sadrzaja sajta. Creative Commons - Autorstvo. ?????? Awesome Inc.. ????????? \x3ca href\x3d\x27https://www.blogger.com\x27 target\x3d\x27_blank\x27\x3eBlogger\x3c/a\x3e.'}, 'displayModeFull'));
</script>
</body>
</html>

Press any key to continue...

Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 22. Async And Await )

Šta su asinhronizovani delegati?

Asinhronizovane metode se u naprednijem programiranju često koriste preko delegata i takve delegate nazivamo asinhronizovane delegate. Za sada shvatite da su delegati samo potpis neke metode poput pointer-a na funkciju u C programskom jeziku ili kao tip; preciznije objekat koji ima referencu na metodu. Ništa toliko komplikovano, samo pojednostavljuje kodiranje. Pogledajte kod.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AsynchronounsDelegates
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<int, int> delegat = LongRunningOperation;
          
            // Execute functions through a delegate
            IAsyncResult async = delegat.BeginInvoke(20, null, null);
            WriteLine("Do something without result...");

            // Is the method completed?
            if (!async.IsCompleted) WriteLine("Still do something...");

            WriteLine("Do something else without result...");

            // Wait for the result
            int result = delegat.EndInvoke(async);
            WriteLine($"Result of 20 = {result}");

            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();


        }

        private static int LongRunningOperation(int loop)
        {
            int counter;

            for (counter = 0; counter < loop; counter++)
            {
                WriteLine(counter);

            }

            return counter * 3;
        }
    }
}

Kao što možete videti u kodu delegati imaju asinhronizovane metode BeginInvoke, IsCompleted i EndIvoke sa kojima preko delegata nastavljate izvršavanje metode kad vam ne treba rezultat, proveravate da li je metoda izvršena ili jednostavno se celi proces zadržava na metodi dok ne dobije rezultat. Kad pokrenete navedeni kod rezultat će biti sledeći:

Do something without result...
Still do something...
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Do something else without result...
Result of 20 = 60

Press any key to continue...

Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 23. Asynchronouns Delegate )