C# alapok III
Párhuzamos programozás¶
Manapság a legtöbb eszköz támogatja a többszálú programvégrehajtást, azaz a program futása több részben, egyidőben is történhet. A gyakorlaton a feladat alapú párhuzamosításról esik szó (Task-based asynchronous programming), ez a megközelítés magasabb absztrakciós lehetőséget nyújt a programozónak.
Két fő előnye van a párhuzamosításnak: hatékonyabb és jobban skálázható alkalmazások készülhetnek. A keretrendszer foglalkozik a futtató szálak mennyiségével, a lehető legjobb teljesítményt ígérve.
Task¶
Mint ahogy a nevéből már sejthető, a Task egy feladat absztrakciója.
Nagy általánosságban két típusú Task-ot különböztetünk meg: amely rendelkezik visszatérési értékkel és amely nem.
Amely nem rendelkezik visszatérési értékkel, az System.Threading.Task.Task típusú, ellenkező esetben pedig ennek leszármazottja: System.Threading.Task.Task<TResult>.
A létrehozott Task objektumtól lekérdezhető a státusza, elindult-e, befejeződött-e, meg lett szakítva, dobott-e kivételt, stb.
Egy Task létrehozásánál egy delegate-t kell megadni, amin keresztül a kívánt kódrészlet elérhető.
1 2 3 4 5 6 7 8 9 10 11 12 | |
A fenti példában a taskA objektum létrehozásakor az nem indul el, ehhez expliciten meg kell hívni a Start metódust.
Ez a két lépés egyben is végrehajtható, ekkor a Task.Run-t kell használni.
1 2 3 4 | |
A Wait metódus implikálja, hogy a Main függvényt futtató szál megvárja a taskA-t, hogy befejezze a munkát.
Amennyiben egy Task rendelkezik visszatérési értékkel, akkor valószínűleg meg is szeretnénk kapni azt.
Egy random mágiát kiszámoló függvény esetében a hívás a következő formában volt: double magic = DoMagic(3.14);.
A függvény lefutott, majd következő utasításnál a magic változóban elérhető a visszaadott (jelen esetben double) érték.
1 2 3 4 5 6 7 8 | |
Több Task elindításához használható a Factory osztály statikus StartNew metódusa.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Amikor a vezérlés eléri taskArray[i].Result-t, akkor az eredményt kérő szál (main) megvárja a feladat befejezését.
A futás során a konzolra kiírt számok a szálak azonosítószámai, így látható, hogy a három különböző Task párhuzamosan fut.
Több Task futtatása során megtörténhet, hogy ugyanazon a szálon futnak, ilyen esetben érdemes a Task.CurrentId-t használni, mely a feladat azonosítójával tér vissza.
Thread¶
A Task egy absztrakció a szálak felett, viszont érdemes ezek működésével is tisztában lenni.
1 2 3 4 5 6 7 8 9 | |
Ha a metódusok az eddig megszokott, szekvenciális módon futnak le, akkor a kimenet nem tartogat semmi meglepőt.
1 2 | |
Amennyiben különböző szálakban futtatjuk a két függvényt, más lesz a konzolra kiírt sorok rendje. Ehhez új szálat kell létrehozni, melynek konstruktorában meg kell adni a futtatni kívánt függvény nevét.
1 2 3 4 5 | |
A t1.Join() metódussal elérhető, hogy az egyik szál megvárja a másikat, míg a Sleep meghatározott idejű várakoztatást tesz lehetővé.
Egy szál megszakításához az Abort függvény használható.
async, await¶
Ez a két kulcsszó lehetővé teszi párhuzamos konstrukciók létrehozását úgy, hogy a kódban szekvenciális program írásának látszata és könnyedsége megmarad.
Ez egy úgynevezett nem-blokkoló futást eredményez, ami grafikus alkalmazások során hasznos. Grafikus alkalmazások esetén nem engedhető meg, hogy a felhasználói felület lefagyjon. Amennyiben a felhasználó elindít egy hosszú folyamatot (pl. letöltés, adatbázisművelet, hosszú számítások elvégzése), a GUI-nak reszponzívnak kell maradnia.
Ez nem csak kényelmi funkció, hanem a UI lefagyás azt is jelentheti a felhasználónak, hogy a teljes munkamenet meghiúsult és kilép.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Jelen esetben a Work metódusban elindul a Task futása, viszont nem várjuk meg.
A konzolra a Returning sum 0 kerül ki, majd a Main függvény is kiírja ugyanezt az értéket.
Ezzel egyidőben a Work: XY is kiírásra kerül (előtte, utána, ahogy sikerül).
Ahhoz, hogy a valós összeget adja vissza a függvény, a következő változtatások szükségesek:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Fájl műveletek¶
A gyakorlatok során még nem jutottunk el adatbázisok kezeléséhez, viszont valamilyen állományokkal való munka nélkülözhetetlen a kötelező program megvalósításához. Ehhez nyújt segítséget a fejezet, így csak szöveges és bináris állományok olvasására és írására fókuszál.
Állományok beolvasása¶
Több módszer áll rendelkezésre az állományok beolvasására.
Első esetben az a fájlban található szöveg egy db string változóba kerül.
1 2 3 | |
A @"<path>" szükséges az útvonalban található \,/ karakterek feloldására, máskülönben escape szekvenciaként szeretné feloldani a fordító.
Amennyiben nem egy szöveges változóban kellene a tartalom, hanem pl. soronként, arra a következő kódrészlet nyújt megoldást.
1 2 3 4 5 6 | |
Ezek kis fájlok esetén jól működnek, viszont amennyiben a rendelkezésre álló memóriával vetekszik az olvasandó fájl, más megoldást kell alkalmazni.
1 2 3 4 5 | |
A StreamReader egy pufferbe olvas, mely elérhető a program számára.
Ez a preferált módszer.
Állományok írása¶
Állományok írására az olvasáshoz hasonlóan három módszer kerül ismeretésre.
1 2 | |
Ebben az esetben második paraméterként egy IEnumerable<string> típusú változót kell átadni, amin végig tud a függévny iterálni.
1 2 3 | |
A WriteAllText szöveges paramétert vár (opcionálisan kódolást, UTF-8), melyet ki tud írni a megadott helyre.
1 2 3 4 5 | |
Bináris fájlokkal való munkához a {StreamReader, StreamWriter}-t le kell cserélni {BinaryReader, BinaryWriter} típusokra.
Hivatkozások¶
C# THREADING AND TYPES OF THREADING STEP BY STEP
Task-based asynchronous programming
How to read a text file one line at a time
Use Visual C# to read from and write to a text file