Grafikus C# – Windows Forms tanfolyam › 11.4 lecke

Windows Forms · 11.4 lecke · blokkolás

Thread.Sleep és a blokkolás problémája

A Thread.Sleep(3000) „csak vár három másodpercet”, és a legegyszerűbb módja annak, hogy a felület szálán megszakítsd a programot. Ez a blokkolás: a szál áll, és közben semmi mást nem csinál, az ablak lefagy (11.3 lecke). A Sleep csak a legismertebb blokkoló hívás: ugyanígy blokkol a .Wait(), a .Result, a Thread.Join() és minden szinkron fájl- vagy hálózati művelet. A blokkolásnak egy különösen alattomos változata a holtpont (deadlock): a program örökre megáll, mert két dolog egymásra vár. Ebben a leckében felismered a blokkoló hívásokat, megérted a klasszikus async + .Result holtpontot, ismered a nem blokkoló alternatívákat (Task.Delay, Timer), és a hibakeresővel megtalálod a blokkolás helyét.

  • kb. 100 perc
  • haladó szint
  • blokkolás és holtpont
Diáklány C# programot készít laptopon

A lecke végére

  • felismered a blokkoló hívásokat (Thread.Sleep, Wait, Result, Join, szinkron I/O) a felületi szálon;
  • érted a holtpont mechanizmusát (az await visszatér a felületi szálra, amely a .Result miatt áll);
  • nem blokkoló alternatívát választasz (await Task.Delay, Timer);
  • ismered a ConfigureAwait(false) szerepét és korlátait;
  • a Visual Studio Break All funkciójával megtalálod, hol áll a felületi szál;
  • elkerülöd a tipikus hibákat (blokkolás a Load-ban, Sleep a Tick-ben, .Result az eseménykezelőben).

Mielőtt elkezded

Ehhez a leckéhez a 11.1–11.3 lecke szükséges. Az async/await alapjait az 11.5 lecke tárgyalja részletesen; itt a hatását mutatjuk meg a blokkolásra.

11.3 UI-szál →

Thread.Sleep a felületi szálon

Form1.cs (using System.Threading;)C#
// ROSSZ a felületi szálon: 3 másodpercig nem rajzol, nem reagál
private void btnRossz_Click(object sender, EventArgs e)
{
lblAllapot.Text = "Várok..."; // nem látszik
Thread.Sleep(3000);
lblAllapot.Text = "Kész.";
}

// JÓ: az eseménykezelő async, a várakozás nem blokkol (a részleteket a 11.5 lecke tárgyalja)
private async void btnJo_Click(object sender, EventArgs e)
{
lblAllapot.Text = "Várok..."; // ez látszik: a felület szál közben szabad
await Task.Delay(3000); // a várakozás ALATT a felület él
lblAllapot.Text = "Kész."; // a folytatás visszatér a felületi szálra
}

// a Thread.Sleep háttérszálban rendben van (az a szál úgysem csinál mást)
new Thread(() => { Thread.Sleep(3000); /* ... */ }) { IsBackground = true }.Start();
Mit csinál a Sleep?A Thread.Sleep(ms) az aktuális szálat az adott időre alvó állapotba teszi: addig semmit sem csinál. A felületi szálon ez a teljes ablak lefagyását jelenti.
await Task.DelayA Task.Delay nem blokkolja a szálat: a várakozás idejére a vezérlést visszaadja a hívónak (a felületi szál a többi üzenetet kezeli), majd az idő letelte után folytatja a következő sort. Eredmény: ugyanaz a késleltetés, élő felülettel.
Háttérszálon a Sleep rendbenHáttérszálon a Sleep nem okoz lefagyást. De akkor is jobb a Task.Delay (megszakítható: CancellationToken, 11.8 lecke).
A tipikus ürügy„Várok egy kicsit, hogy az adat megérkezzen”: a Sleep-pel találgatott várakozás törékeny (túl rövid vagy fölöslegesen hosszú). Jobb a tényleges jelzésre (esemény, await) várni.

Jósolj!

A Form1_Load eseménykezelőben Thread.Sleep(5000) áll („hogy lássák a nyitóképet”). Mit tapasztal a felhasználó a program indításakor?

Jóslat ellenőrzése

5 másodpercig nem jelenik meg az ablak (vagy üres, fehér marad), mert a Load a megjelenítés előtt fut, a felületi szálon. A nyitóképhez külön splash ablak vagy időzítő kell, nem Sleep.

A blokkoló hívások családja

HívásMit csinál?Alternatíva
Thread.Sleep(ms)A szál alszik.await Task.Delay(ms), Timer
task.Wait()A szál várja a Task végét.await task
task.ResultVárja az eredményt (blokkol).var x = await task;
task.GetAwaiter().GetResult()Ugyanaz, kicsit másképp.await
thread.Join()Várja a másik szál végét.Task + await, RunWorkerCompleted
File.ReadAllText hálózati megosztáson, WebClient.DownloadString, szinkron HttpClient.SendSzinkron I/O: addig vár, amíg a hálózat/lemez válaszol (akár másodpercekig).await File.ReadAllTextAsync, await client.GetStringAsync
Process.WaitForExit()Várja a külső program végét.await process.WaitForExitAsync() (újabb .NET)
lock, Monitor.Enter hosszan tartott zárraVárja, hogy a másik szál elengedje a zárat.Rövid zárak; a felületi szálon kerüld.
A szabályA felületi szálon minden hívás legyen gyors (< 50–100 ms). Amit nem tudsz garantálni (hálózat, lemez, külső program), az ne a felületi szálon, blokkolva fusson.
Szinkron I/OA fájl- és hálózati műveletek sebessége a környezettől függ: a fejlesztői gépen 2 ms, a hálózati megosztáson vagy lassú pendrive-on 5 másodperc. A kód „működik nálam” módon tesztelve rejtett lefagyást hagy a felhasználóra.
Rejtett blokkolásNéha nem látszik: egy getter, amely a háttérben adatbázist hív, egy Clipboard.GetText() (más programra vár), egy Directory.GetFiles nagy mappán. A hibakereső segít megtalálni (lásd lent).
A MessageBox sem blokkolja a rajzolástA MessageBox.Show a háttérben üzenetsor-feldolgozást folytat (modális ciklus), ezért az ablak újra tud rajzolódni; de a hívó kód addig áll. Ezért is jó, ha a hosszú munka nem a felület gombnyomásában „ül”.

Jósolj!

A program a Directory.GetFiles(@"\\\\szerver\\megosztas", "*.*", SearchOption.AllDirectories) hívással egy hálózati megosztást listáz a gombnyomásra, és a hálózat lassú. Mit tapasztal a felhasználó?

Jóslat ellenőrzése

Lefagyó ablakot (másodpercekig, akár percekig): a szinkron fájlrendszer-hívás blokkolja a felületi szálat. A fejlesztő a gyors helyi lemezen sosem látta. A műveletet háttérszálra (Task.Run, 11.6 lecke) kell tenni, és megszakíthatóvá.

A holtpont: await és .Result egymásra vár

A leggyakoribb és legalattomosabb holtpont így keletkezik: az eseménykezelő a felületi szálon szinkron módon várja egy async metódus eredményét (.Result vagy .Wait()), az async metódus pedig az await után vissza akar térni a felületi szálra. Mindkettő a másikra vár:

Form1.csC#
private async Task<int> AdatAsync()
{
await Task.Delay(500); // az await UTÁN a folytatás a felületi szálon akar futni

return 42;
}

// HOLTPONT: a felületi szál itt áll és vár (.Result), a folytatás is rá vár
private void btnHoltpont_Click(object sender, EventArgs e)
{
int x = AdatAsync().Result; // ↯ a program örökre megáll

lblAllapot.Text = "Eredmény: " + x; // ide sosem jut el
}

// HELYES: az egész lánc async ("await végig")
private async void btnHelyes_Click(object sender, EventArgs e)
{
int x = await AdatAsync(); // nem blokkol: a felületi szál szabad marad, a folytatás lefuthat

lblAllapot.Text = "Eredmény: " + x;
}
Miért áll?Az await alapból megjegyzi a környezetet (SynchronizationContext: Windows Formsban a felületi szál), és a folytatást oda üzeni vissza. A .Result közben a felületi szál áll, ezért nem tudja kiszedni az üzenetsorból a folytatást. A Result a befejezésre vár, a befejezés a felületi szálra: kör, nincs kiút.
Az ellenszer: „await végig”Az async metódusokat await segítségével hívd, az eseménykezelőt is async void alakban (csak eseménykezelő lehet async void). Így soha nem blokkolsz a felületi szálon.
ConfigureAwait(false)Az await task.ConfigureAwait(false) azt mondja: a folytatás tetszőleges szálon futhat, nem kell visszamenni a felületi szálra. Ez feloldja a holtpontot, de csak könyvtári (nem felületet érintő) kódban megfelelő, mert utána a vezérlők már nem érhetők el. Eseménykezelőben nem használjuk.
A tünetNincs hibaüzenet, nincs kivétel: a program egyszerűen megáll az adott sorban, a processzorhasználat nulla. Ez a holtpont árulkodó jele (a lefagyott ciklus 100%-ot használna).

Jósolj!

A fenti AdatAsync metódus await Task.Delay(500).ConfigureAwait(false) alakban íródik, és az eseménykezelő a .Result segítségével várja. Megáll-e a program?

Jóslat ellenőrzése

Nem áll meg (kb. 0,5 másodperc után folytatódik): a folytatás nem akar a felületi szálra visszatérni, ezért befejezhet, miközben a felületi szál a .Result-ra vár. A felület közben így is 0,5 másodpercig fagyott volt: a holtpont megszűnt, a blokkolás nem. A javítás az await végig.

Megoldások és a blokkolás megtalálása

Form1.csC#
// 1. egyszeri késleltetés: Timer (11.1) vagy await
private async void btnMentes_Click(object sender, EventArgs e)
{
tsslUzenet.Text = "Mentve";
await Task.Delay(3000);
tsslUzenet.Text = ""; // az üzenet 3 másodperc múlva eltűnik, a felület közben él
}

// 2. sorozat késleltetéssel: egyszerűbb await-tel, mint időzítő-állapotgéppel
private async void btnDemo_Click(object sender, EventArgs e)
{
for (int i = 3; i > 0; i--)
{
lblSzamlalo.Text = i.ToString();
await Task.Delay(1000);
}

lblSzamlalo.Text = "Indul!";
}

// 3. szinkron fájlművelet helyett aszinkron (újabb .NET)
private async void btnOlvas_Click(object sender, EventArgs e)
{
btnOlvas.Enabled = false;

try { txtTartalom.Text = await File.ReadAllTextAsync(ut); }
catch (IOException ex) { tsslUzenet.Text = ex.Message; }
finally { btnOlvas.Enabled = true; }
}

Hol blokkol a program? (Visual Studio)

Ha a program lefagyott: a Visual Studióban Debug → Break All (Ctrl+Alt+Break). A hibakereső megállítja a programot, és pontosan azt a sort mutatja, ahol a felületi szál áll (.Result, Sleep, ReadAllText...). A Debug → Windows → Threads ablak és a Call Stack a hívási láncot adja.

Megelőzés

Egyszerű szabályok: a felületi szálon nincs Sleep, .Result, .Wait(), szinkron hálózat és lassú fájlművelet; az eseménykezelők gyorsak vagy async; a tesztelés lassú környezetben (hálózati meghajtó, nagy mappa) is megtörténik.

Jósolj!

Egy async void eseménykezelő await Task.Delay(3000) sora alatt a felhasználó másodszor is rákattint a gombra. Mi történik?

Jóslat ellenőrzése

A második kattintás új, párhuzamos futást indít (az await közben a felület szabad, és a kezelő újra meghívható): két „példány” fut, felülírhatják egymás eredményét. Az await nem blokkol, de nem is tiltja az újrabelépést: a gombot le kell tiltani (Enabled = false), vagy ellenőrizni kell egy jelzőt.

📷 Képernyőkép helye

Mit tegyél ide: a blokkolás-vadász program: a „Holtpont gyanú” üzenet a Wait(2000) időtúllépés után, mellette a Visual Studio Break All nézete.Javasolt fájlnév: winforms-11-4-blokkolas.webp · Alt szöveg: A blokkolás-vadász program

Hibakeresés: te vagy a hibadetektív

Mi a baj, és hogyan javítod?

Gondold végig az okot, és csak utána nyisd le a megoldást.

1. A program megáll, a processzor 0%

string s = LetoltAsync(url).Result;   // az eseménykezelőben
Hiba és javítás

Holtpont: a .Result a felületi szálat blokkolja, az await folytatása pedig oda akar visszatérni. Írd át await-re (az eseménykezelő async void).

2. A nyitóablak 5 másodpercig üres

private void Form1_Load(object sender, EventArgs e) { Thread.Sleep(5000); }
Hiba és javítás

A Load a megjelenítés előtt fut a felületi szálon. Ne várakozz benne: az adatot a Shown esemény után, aszinkron töltsd be.

3. A Tick-ben Sleep van

private void tmr_Tick(object sender, EventArgs e) { Thread.Sleep(2000); }
Hiba és javítás

A Tick a felületi szálon fut: az időzítő célja pont az, hogy ne blokkolj. Állítsd az Interval értékét a kívánt késleltetésre, vagy használj await Task.Delay metódust.

4. Kétszeri indítás az await alatt

A gyors dupla kattintás két párhuzamos letöltést indít.

Ok és javítás

Az await felszabadítja a felületet, és a kezelő újra hívható. Tiltsd le a gombot a futás idejére (try–finally blokkal visszaengedve).

5. Join a felületi szálon

szal.Start();
szal.Join(); // a felületi szál várja a másikat; ha az Invoke-ot hív: holtpont
Hiba és javítás

A Join blokkolja a felületi szálat; ha a háttérszál Invoke segítségével a felületre vár, kölcsönös várakozás (holtpont) keletkezik. Ne várj a szálra: kérj értesítést a befejezésről (await, RunWorkerCompleted, BeginInvoke).

6. A ConfigureAwait(false) után a vezérlő hibát ad

await Task.Delay(500).ConfigureAwait(false);
lblAllapot.Text = "Kész"; // már nem a felületi szálon
Hiba és javítás

A ConfigureAwait(false) után a folytatás tetszőleges szálon fut: a vezérlő elérése InvalidOperationException. Eseménykezelőben ne használd.

Mini projekt: blokkolás-vadász

Készíts kísérleti programot a blokkoló hívások és a nem blokkoló megfelelőik összehasonlítására:

  1. Felület: a bal oldalon rossz változatok (Sleep (3 mp), Szinkron fájlolvasás (lassú), Holtpont (.Wait)), a jobb oldalon a jó megfelelők (await Delay (3 mp), Aszinkron fájlolvasás, await végig, ConfigureAwait(false)); egy mozgó négyzet animáció (tmrAnim, 20 ms) és egy késésmérő (tmrMero, 50 ms: a legnagyobb késés, 11.3 lecke); lstNaplo; Nullázás.
  2. Sleep vs Delay: az rossz gomb a felületi szálon Thread.Sleep(3000) hívást végez (az animáció megáll, a késés ≈ 3000 ms); a jó gomb await Task.Delay(3000) hívást (az animáció fut, a késés kicsi). A naplóban a mért legnagyobb késés mindkét esetre.
  3. Szinkron/aszinkron fájlolvasás: a „lassú fájl” szimulálása: egy 40 MB-os ideiglenes fájl létrehozása (Path.GetTempFileName), és olvasás File.ReadAllText (blokkol), illetve await File.ReadAllTextAsync (nem blokkol); az aszinkron változatnál az animáció fut; az ideiglenes fájl törlése finally-ban.
  4. Holtpont (biztonságosan): az AdatAsync (await Task.Delay(500)); a .Wait(2000) időtúllépéssel (bool kesz = AdatAsync().Wait(2000)): hamis eredménynél a napló HOLTPONT GYANÚ: a .Wait nem fejeződött be 2 mp alatt; közben a felület 2 másodpercig fagyott (az animáció áll).
  5. await végig és ConfigureAwait: az await végig gomb helyesen működik; a ConfigureAwait(false) változat a .Wait(2000) mellett is befejeződik (de a felület közben fagyott: a napló jelzi), és a folytatás szálát (Environment.CurrentManagedThreadId) összeveti a felületi szállal.
  6. Újrabelépés: az await Delay gomb dupla kattintásra két futást indít: a napló ÚJRABELÉPÉS üzenete; az Egyszer jelölőnégyzet a gombot letiltja a futás idejére (try–finally).
  7. Hibakeresés gyakorlása: a leírásban lépésről lépésre: indítsd a holtpontot időtúllépés nélkül (külön, jól jelölt Veszélyes! gomb, megerősítéssel), majd Visual Studióban Break All, és jegyezd fel, melyik sorban áll a felületi szál (a napló üzenetébe nem kerül: ez jegyzet).
  8. Teszteld: minden gomb 3 mp-es késleltetéssel, a késésmérő értékei, dupla kattintás, bezárás futás közben, a fájl törlése hiba esetén is.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A központi rész a késésmérő, a Sleep / Delay összevetés és a biztonságos holtpont-felismerés Wait(2000) időtúllépéssel.

Form1.cs (using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using System.IO;)C#
private Stopwatch meroOra = Stopwatch.StartNew();
private long utolsoTick = 0, legnagyobb = 0;
private int negyzetX = 0, irany = 4;
private int futo = 0;
private int felulet;

private void Form1_Load(object sender, EventArgs e)
{
felulet = Environment.CurrentManagedThreadId;
tmrMero.Interval = 50;
tmrMero.Start();
tmrAnim.Interval = 20;
tmrAnim.Start();
}

private void tmrMero_Tick(object sender, EventArgs e)
{
long most = meroOra.ElapsedMilliseconds;
long keses = most - utolsoTick - tmrMero.Interval;

utolsoTick = most;

if (keses > legnagyobb) { legnagyobb = keses; }

lblKeses.Text = "Legnagyobb késés: " + legnagyobb + " ms";
}

private void tmrAnim_Tick(object sender, EventArgs e)
{
negyzetX += irany;

if (negyzetX < 0 || negyzetX > pnlAnim.Width - 30) { irany = -irany; negyzetX += irany; }

pnlNegyzet.Left = negyzetX;
}

private void Naploz(string s) { lstNaplo.Items.Insert(0, DateTime.Now.ToString("HH:mm:ss") + " " + s); }

private void btnNulla_Click(object sender, EventArgs e) { legnagyobb = 0; lstNaplo.Items.Clear(); }

// ---- Sleep vs Delay ----
private void btnSleep_Click(object sender, EventArgs e)
{
Naploz("Sleep indul");
Thread.Sleep(3000); // blokkol
Naploz("Sleep kész: az animáció állt, a késés ≈ 3000 ms");
}

private async void btnDelay_Click(object sender, EventArgs e)
{
futo++;

if (futo > 1) { Naploz("ÚJRABELÉPÉS: már fut " + (futo - 1) + " példány"); }

if (chkEgyszer.Checked) { btnDelay.Enabled = false; }

try
{
Naploz("await Delay indul");
await Task.Delay(3000); // nem blokkol
Naploz("await Delay kész: az animáció futott");
}
finally
{
futo--;
btnDelay.Enabled = true;
}
}

// ---- szinkron és aszinkron fájlolvasás ----
private string nagyFajl()
{
string ut = Path.GetTempFileName();

File.WriteAllText(ut, new string('x', 40 * 1024 * 1024));

return ut;
}

private void btnSzinkron_Click(object sender, EventArgs e)
{
string ut = nagyFajl();

try
{
Stopwatch sw = Stopwatch.StartNew();
string s = File.ReadAllText(ut); // blokkol

Naploz("Szinkron olvasás: " + s.Length + " karakter, " + sw.ElapsedMilliseconds + " ms (a felület fagyott)");
}
finally { File.Delete(ut); }
}

private async void btnAszinkron_Click(object sender, EventArgs e)
{
string ut = nagyFajl();

btnAszinkron.Enabled = false;

try
{
Stopwatch sw = Stopwatch.StartNew();
string s = await File.ReadAllTextAsync(ut);

Naploz("Aszinkron olvasás: " + s.Length + " karakter, " + sw.ElapsedMilliseconds + " ms (a felület élt)");
}
catch (IOException ex) { Naploz("Hiba: " + ex.Message); }
finally { File.Delete(ut); btnAszinkron.Enabled = true; }
}

// ---- holtpont ----
private async Task<int> AdatAsync()
{
await Task.Delay(500);

return 42;
}

private async Task<int> AdatCfgAsync()
{
await Task.Delay(500).ConfigureAwait(false);

Naploz2("ConfigureAwait(false) folytatás szála: " + Environment.CurrentManagedThreadId + " (felületi: " + felulet + ")");

return 42;
}

private void Naploz2(string s)
{
// a folytatás másik szálon futhat: a vezérlőt BeginInvoke-kal érjük el
try { BeginInvoke(new Action(() => Naploz(s))); }
catch (InvalidOperationException) { }
}

private void btnHoltpont_Click(object sender, EventArgs e)
{
Stopwatch sw = Stopwatch.StartNew();
bool kesz = AdatAsync().Wait(2000); // időtúllépéssel: nem fagy le örökre

Naploz(kesz ? "A .Wait befejeződött" : "HOLTPONT GYANÚ: a .Wait nem fejeződött be 2 mp alatt (" + sw.ElapsedMilliseconds + " ms fagyás)");
}

private void btnConfig_Click(object sender, EventArgs e)
{
Stopwatch sw = Stopwatch.StartNew();
bool kesz = AdatCfgAsync().Wait(2000);

Naploz(kesz ? "ConfigureAwait(false): a .Wait befejeződött (" + sw.ElapsedMilliseconds + " ms), de a felület közben fagyott!" : "Időtúllépés");
}

private async void btnVegig_Click(object sender, EventArgs e)
{
btnVegig.Enabled = false;

try
{
int x = await AdatAsync(); // await végig: nincs holtpont, nincs fagyás

Naploz("await végig: eredmény " + x + " (a felület élt)");
}
finally { btnVegig.Enabled = true; }
}

private void btnVeszelyes_Click(object sender, EventArgs e)
{
if (MessageBox.Show(this, "Ez a gomb TÉNYLEGES holtpontot okoz: a program örökre megáll, csak a Feladatkezelővel állítható le (vagy Break All a Visual Studióban). Folytatod?", "Veszélyes kísérlet", MessageBoxButtons.YesNo, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button2) != DialogResult.Yes) { return; }

int x = AdatAsync().Result; // ↯ a gyakorlathoz: Break All
}
📷 Képernyőkép helye

Mit tegyél ide: a kész blokkolás-vadász a mért késésekkel.Javasolt fájlnév: winforms-11-4-kesz.webp · Alt szöveg: A kész blokkolás-vadász

Három önálló gyakorlófeladat

Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.

1. Késleltetett üzenet

A gombnyomás után a program Üdv! üzenetet ír ki, 2 másodperc múlva eltünteti. Írd meg Timerrel és await-tel is.

Egy lehetséges megoldás

A Timer: Interval = 2000, a Tick-ben Stop() és az üzenet törlése; await: lbl.Text = "Üdv!"; await Task.Delay(2000); lbl.Text = ""; (async void kezelőben). Az await változat rövidebb.

2. Lassú hívás keresése

A program a gombnyomásra „sokáig gondolkodik”. Hogyan találod meg a blokkoló sort?

Egy lehetséges megoldás

Debug → Break All a fagyás közben: a Call Stack felső eleme a blokkoló sor. Alternatíva: Stopwatch mérés a gyanús hívás körül, és a naplóba írás.

3. Szinkron wrapper aszinkron helyett

Egy könyvtári metódus Task<string> LetoltAsync(). Egy kolléga a LetoltAsync().Result hívással „csomagolja” szinkron metódusba. Miért veszélyes?

Egy lehetséges megoldás

Felületi szálról hívva holtpontot okozhat; háttérszálról hívva szálat köt le. A tanács: a hívási lánc egésze legyen async; szinkron környezetből (konzol Main) elfogadható a .GetAwaiter().GetResult().

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Blokkol-e?

Blokkol-e a felületi szálon: (a) await Task.Delay(1000); (b) Thread.Sleep(1000); (c) task.Result?

Megoldás megnyitása

(a) nem; (b) igen; (c) igen (és holtpontot is okozhat).

2. A holtpont tünete

Mit tapasztalsz holtpontnál: magas processzorhasználat vagy 0%?

Megoldás megnyitása

Közel 0%: a szálak állnak és várnak egymásra (nincs számítás). A magas processzorhasználatú fagyás végtelen ciklus.

Közepes szint

3. async void

Mikor szabad async void metódust írni?

Megoldás megnyitása

Csak eseménykezelőnél. Máshol async Task kell (hogy a hívó várhasson rá, és a kivétel eljusson hozzá).

4. Timer vagy Task.Delay?

Melyiket választod: (a) 1 másodpercenként frissülő óra; (b) egyszeri 3 másodperces késleltetés?

Megoldás megnyitása

(a) Timer (ismétlődő); (b) await Task.Delay(3000) (egyszeri, egyszerűbb).

Kihívás szint

5. Holtpont két zárral

Két szál két zárat ellentétes sorrendben vesz el. Mi történik, és mi a szabály?

Megoldás megnyitása

A klasszikus holtpont: az A szál az 1. zárat tartja és a 2.-ra vár, a B a 2.-at tartja és az 1.-re vár. Szabály: a zárakat mindig azonos sorrendben vedd el, és rövid ideig tartsd.

6. Tesztelés lassú környezetben

Hogyan tudod a fejlesztői gépen szimulálni a lassú hálózatot a fájlműveletekhez?

Megoldás megnyitása

Szándékos késleltetés a hívás körül (Thread.Sleep a hívó metódus belsejében, csak tesztelésre), lassú meghajtó vagy hálózati megosztás használata, vagy a fájlrendszer „csomagolása” (interfész mögött), amelyben a késleltetés be- és kikapcsolható.

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Mi a blokkolás?

A szál áll, és közben semmi mást nem csinál; a felületi szálon ez fagyást okoz.

2. Miért jobb az await Task.Delay?

Nem blokkolja a szálat: a várakozás alatt a felület él, utána a folytatás visszatér a felületi szálra.

3. Hogyan keletkezik az async + .Result holtpont?

A felületi szál a .Result-ra vár, az await folytatása a felületi szálra akar visszatérni: kölcsönös várakozás.

4. Mi az „await végig” szabálya?

A hívási lánc minden szintjén await, az eseménykezelő async void; sehol .Result/.Wait().

5. Mit tesz a ConfigureAwait(false)?

A folytatás nem tér vissza a felületi szálra; feloldja a holtpontot, de eseménykezelőben a vezérlők elérését elrontja.

6. Hogyan találod meg, hol áll a program?

Visual Studio: Debug → Break All, és a Call Stack felső sora.

7. Mit okoz a második kattintás await közben?

Párhuzamos újrabelépést: a gombot le kell tiltani a futás idejére.

Mit tudsz már?

  • Felismered a blokkoló hívásokat, és nem használod őket a felületi szálon.
  • Érted a holtpont mechanizmusát, és az „await végig” szabállyal elkerülöd.
  • Nem blokkoló alternatívát (Task.Delay, Timer) választasz.
  • Ismered a ConfigureAwait(false) szerepét és korlátait.
  • A hibakeresővel (Break All) megtalálod a blokkolás helyét.

Következő lecke: Task, async és await

Az aszinkron programozás alapjai: Task, Task<T>, async/await, kivételek, és az első valódi háttérmunka.