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