Grafikus C# – Windows Forms tanfolyam › 11.4 lecke Windows Forms · 11.4 lecke · blokkolás A Thread.Sleep és a blokkolás problémája
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.
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
awaitvisszatér a felületi szálra, amely a.Resultmiatt á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,SleepaTick-ben,.Resultaz 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.
Thread.Sleep a felületi szálon
// 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();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.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.Sleep nem okoz lefagyást. De akkor is jobb a Task.Delay (megszakítható: CancellationToken, 11.8 lecke).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ás | Mit 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.Result | Vá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.Send | Szinkron 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árra | Várja, hogy a másik szál elengedje a zárat. | Rövid zárak; a felületi szálon kerüld. |
Clipboard.GetText() (más programra vár), egy Directory.GetFiles nagy mappán. A hibakereső segít megtalálni (lásd lent).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:
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;
}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.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.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.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
// 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.
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őbenHiba é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. Az Ok és javítás
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: holtpontHiba é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álonHiba é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:
- 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. - 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ó gombawait 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. - 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ásFile.ReadAllText(blokkol), illetveawait File.ReadAllTextAsync(nem blokkol); az aszinkron változatnál az animáció fut; az ideiglenes fájl törlésefinally-ban. - 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). - 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. - Ú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). - 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).
- 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.
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
}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 A await-tel is.Egy lehetséges megoldás
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? Debug → Break All a fagyás közben: a Call Stack felső eleme a blokkoló sor. Alternatíva: Egy lehetséges megoldás
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 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 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
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) (a) nem; (b) igen; (c) igen (és holtpontot is okozhat).await Task.Delay(1000); (b) Thread.Sleep(1000); (c) task.Result?Megoldás megnyitása
2. A holtpont tünete
Mit tapasztalsz holtpontnál: magas processzorhasználat vagy 0%? 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.Megoldás megnyitása
Közepes szint
3. async void
Mikor szabad Csak eseménykezelőnél. Máshol async void metódust írni?Megoldás megnyitása
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? (a) Megoldás megnyitása
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? 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.Megoldás megnyitása
6. Tesztelés lassú környezetben
Hogyan tudod a fejlesztői gépen szimulálni a lassú hálózatot a fájlműveletekhez? Szándékos késleltetés a hívás körül (Megoldás megnyitása
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. A szál áll, és közben semmi mást nem csinál; a felületi szálon ez fagyást okoz. 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. A felületi szál a A hívási lánc minden szintjén 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. Visual Studio: Debug → Break All, és a Call Stack felső sora. Párhuzamos újrabelépést: a gombot le kell tiltani a futás idejére.1. Mi a blokkolás?
2. Miért jobb az await Task.Delay?
3. Hogyan keletkezik az async + .Result holtpont?
.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?
await, az eseménykezelő async void; sehol .Result/.Wait().5. Mit tesz a ConfigureAwait(false)?
6. Hogyan találod meg, hol áll a program?
7. Mit okoz a második kattintás await közben?
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.



