Grafikus C# – Windows Forms tanfolyam › 11.9 lecke Windows Forms · 11.9 lecke · záró projekt A 11. fejezet záró projektjében mindent egy programba építesz, amit az időzítőkről, az állapotjelzésről, a felületi szálról és az aszinkron munkáról tanultál: egy mappa-elemzőt, amely nagy mappafákat is feldolgoz a felület lefagyása nélkül. Kétfázisú folyamatjelzést ad (fájlok keresése, majd feldolgozás), becsli a hátralévő időt, szüneteltethető, megszakítható, időtúllépést is kezel, és a végén kimutatást ad a legnagyobb fájlokról és a kiterjesztésekről.Mini projekt: folyamatkijelző

A projekt végére
- kétfázisú (
Marquee, majd százalékos) folyamatjelzést építesz; - a munkát
Task.Runsegítségével a háttérbe teszed, a felületet időzítővel frissíted; - szünetet (
ManualResetEventSlim) és megszakítást (CancellationToken) valósítasz meg; - kezeled a hibákat (hozzáférés nélküli mappák), és részeredményt adsz megszakításkor;
- a programot a Form bezárásakor is rendben leállítod.
Mielőtt elkezded
Ehhez a projekthez a 11.1–11.8 lecke szükséges, és a 10.1 (Directory, FileInfo), 10.4 (mappaválasztó) lecke.
Követelménytábla (100 pont)
| Követelmény | Pont |
|---|---|
| Felület: mappa megadása és tallózás, alkönyvtárak jelölő, időtúllépés beállítása, Indítás, Szünet / Folytatás, Mégse; folyamatjelző, állapot-, százalék-, aktuális elem- és időcímkék; eredménylisták, napló; állapotsor. | 10 |
Háttérmunka: a mappabejárás és a feldolgozás háttérszálon (Task.Run), a felület közben él; vezérlőt a háttérszál nem ér. | 15 |
Kétfázisú jelzés: 1. fázis Marquee + talált fájlok száma; 2. fázis százalékos jelző, 12 / 480 (3%), becsült hátralévő idő (5% és 3 mp után), aktuális fájl rövidítve; a végén Kész. | 15 |
Felületfrissítés: az adatokat a háttérszál szálbiztosan teszi közzé (Interlocked), a felületet egy 200 ms-os időzítő frissíti (nincs elárasztott üzenetsor). | 10 |
Szünet és folytatás: ManualResetEventSlim; a szünet alatt az eltelt idő megáll; a gombok állapota helyes. | 10 |
Megszakítás és időtúllépés: CancellationToken, összekapcsolt forrás, a megszakítás oka kiírva, részeredmény látszik; három állapot (Kesz, Fut/Szunet, Megszakitas). | 15 |
| Eredmény: összméret, fájlok és mappák száma, a 10 legnagyobb fájl, kiterjesztések szerinti statisztika (darab és méret), a hibák összegzése. | 10 |
| Hibatűrés: hozzáférés nélküli mappa és fájl kihagyva (nem áll le), a szimbolikus hivatkozás nem okoz végtelen ciklust, nincs kivétel bezáráskor, nem létező mappa érthető üzenet. | 10 |
| Kód és tesztelés: áttekinthető metódusok és nevek, magyarázó megjegyzések, a tesztlista kitöltve. | 5 |
Felületterv
| Vezérlő | Név | Beállítás |
|---|---|---|
TextBox, Button | txtMappa, btnTallozas | a mappa útvonala; a gomb a FolderBrowserDialog-ot nyitja (10.4 lecke) |
CheckBox, NumericUpDown | chkAlkonyvtar, nudIdo | alkönyvtárak (alapból bejelölve); időtúllépés mp-ben (0–3600, 0 = nincs) |
Button ×3 | btnIndit, btnSzunet, btnMegse | Alt+I, Alt+Sz, Esc gyorsbillentyű; kezdetben a Szünet és a Mégse tiltott |
ProgressBar | pb | AccessibleName = "Mappa feldolgozása" |
Label ×4 | lblAllapot, lblSzazalek, lblMostani, lblIdo | fázis és állapot; 12 / 480 (3%), még kb. 40 mp; az aktuális elem; eltelt idő |
ListBox ×3 | lstLegnagyobb, lstKiterjesztes, lstNaplo | a 10 legnagyobb fájl; kiterjesztések; hibák és események |
StatusStrip | tsslUzenet | az utolsó üzenet |
Timer | tmrKijelzo | Interval = 200 |
Mit tegyél ide: a felületterv vázlata vagy a kész mappa-elemző futás közben.Javasolt fájlnév: winforms-11-9-felulet.webp · Alt szöveg: A mappa-elemző felülete
A 8 lépés támponttal
Haladj lépésről lépésre, és minden lépés után futtasd a programot. A támpont segít elindulni, de a kódot magad írd meg. Helyezd el a vezérlőket, állítsd a gyorsbillentyűket. Írd meg a Készíts mezőket a háttérszál és a felület közti adatokhoz: Egy Fájlonként Az Indítás Egy Indításonként új A befejezés után a 10 legnagyobb fájl és a kiterjesztések kimutatása (méret szerint csökkenően), az összméret emberi formában (1,24 GB), a hibák összegzése. Gyorsbillentyűk, 1. lépés: felület és állapotok
Beallit(Allapot) metódust (Kesz, Fut, Szunet, Megszakitas), amely a gombokat engedélyezi vagy tiltja. Támpont: 11.8 lecke, állapotkezelés.2. lépés: a közös adatok
long szamolt, feldolgozott, osszes, osszMeret (Interlocked), volatile int fazis, volatile string aktualis. Egy Eredmeny osztály gyűjti a kimutatást (csak a háttérszál írja, a befejezés után olvassa a felület). Támpont: 11.7 lecke.3. lépés: az 1. fázis: a fájlok keresése
Stack<string> mappaverem, Directory.EnumerateFiles és EnumerateDirectories; a hozzáférés nélküli mappát (UnauthorizedAccessException) naplózd és hagyd ki; a szimbolikus hivatkozást (FileAttributes.ReparsePoint) ne kövesd. A talált fájlok számát Interlocked.Increment növeli. Támpont: 10.1 lecke.4. lépés: a 2. fázis: a feldolgozás
FileInfo.Length; a kiterjesztések szótára (darab, méret); a 10 legnagyobb fájl listája (a lista 20 elem fölött rendezve és levágva); a számlálók Interlocked-dal. A hibás fájl (IOException) kihagyva és megszámolva.5. lépés: Task.Run és a felület frissítése
async void; a bemenetet a felületi szálon olvasod ki; await Task.Run(() => Dolgoz(...)). A 200 ms-os tmrKijelzo olvassa a számlálókat, és frissíti a vezérlőket: az 1. fázisban Marquee, a 2.-ban százalékos jelző és hátralévő idő. Támpont: 11.2 és 11.7 lecke.6. lépés: szünet és folytatás
ManualResetEventSlim szunet = new ManualResetEventSlim(true); a Szünet Reset(), a Folytatás Set(); a háttérmunka minden fájl előtt szunet.Wait(token) hívást végez (áll, amíg szünet van, de a megszakítás kiszabadítja). A Stopwatch a szünetben megáll.7. lépés: megszakítás, időtúllépés, bezárás
CancellationTokenSource és egy időzítős, összekapcsolt forrás (CreateLinkedTokenSource); külön OperationCanceledException ág (ki szakította meg?); részeredmény kiírása; a finally felszabadít és nulláz; a FormClosing megszakít és megvár. Támpont: 11.8 lecke.8. lépés: az eredmény és a finomítás
AccessibleName, hibatűrés: nem létező mappa, üres mappa (0 fájl: ne legyen ArgumentOutOfRangeException a pb.Maximum miatt).
Mintamegoldás
Csak akkor nyisd meg, ha már megpróbáltad. A megoldás egyik lehetséges változat: a vezérlőket a Felületterv neveivel kell létrehoznod.Teljes minta: Form1.cs
private class KiterjStat { public long Db; public long Meret; }
private class Eredmeny
{
public long Fajlok, Mappak, Hibak, OsszMeret;
public Dictionary<string, KiterjStat> Kiterj = new Dictionary<string, KiterjStat>();
public List<KeyValuePair<string, long>> Legnagyobbak = new List<KeyValuePair<string, long>>();
public List<string> HibaSzovegek = new List<string>();
}
private enum Allapot { Kesz, Fut, Szunet, Megszakitas }
private Allapot allapot = Allapot.Kesz;
private CancellationTokenSource forras;
private ManualResetEventSlim szunet = new ManualResetEventSlim(true);
private Stopwatch ora = new Stopwatch();
private bool bezarasra = false;
// a háttérszál és a felület közös adatai
private long szamolt, feldolgozott, osszes, osszMeret;
private volatile int fazis;
private volatile string aktualis = "";
private void Form1_Load(object sender, EventArgs e)
{
tmrKijelzo.Interval = 200;
pb.AccessibleName = "Mappa feldolgozása";
Beallit(Allapot.Kesz);
}
private void Beallit(Allapot a)
{
allapot = a;
btnIndit.Enabled = a == Allapot.Kesz;
btnSzunet.Enabled = a == Allapot.Fut || a == Allapot.Szunet;
btnMegse.Enabled = a == Allapot.Fut || a == Allapot.Szunet;
txtMappa.Enabled = a == Allapot.Kesz;
btnTallozas.Enabled = a == Allapot.Kesz;
chkAlkonyvtar.Enabled = a == Allapot.Kesz;
nudIdo.Enabled = a == Allapot.Kesz;
btnSzunet.Text = a == Allapot.Szunet ? "Folytatás" : "Szünet";
this.UseWaitCursor = a == Allapot.Fut || a == Allapot.Megszakitas;
if (a == Allapot.Megszakitas) { lblAllapot.Text = "Megszakítás folyamatban..."; }
}
private void btnTallozas_Click(object sender, EventArgs e)
{
using (FolderBrowserDialog d = new FolderBrowserDialog())
{
if (Directory.Exists(txtMappa.Text)) { d.SelectedPath = txtMappa.Text; }
if (d.ShowDialog(this) == DialogResult.OK) { txtMappa.Text = d.SelectedPath; }
}
}
private async void btnIndit_Click(object sender, EventArgs e)
{
string mappa = txtMappa.Text.Trim();
if (!Directory.Exists(mappa)) { tsslUzenet.Text = "A megadott mappa nem létezik."; return; }
bool alkonyvtar = chkAlkonyvtar.Checked;
int idoMp = (int)nudIdo.Value;
// minden indításnál nullázunk
Interlocked.Exchange(ref szamolt, 0);
Interlocked.Exchange(ref feldolgozott, 0);
Interlocked.Exchange(ref osszes, 0);
Interlocked.Exchange(ref osszMeret, 0);
aktualis = "";
fazis = 1;
szunet.Set();
lstLegnagyobb.Items.Clear(); lstKiterjesztes.Items.Clear(); lstNaplo.Items.Clear();
lblSzazalek.Text = ""; lblMostani.Text = "";
pb.Style = ProgressBarStyle.Marquee; // 1. fázis: határozatlan
pb.MarqueeAnimationSpeed = 30;
lblAllapot.Text = "Fájlok keresése...";
CancellationTokenSource felhasznalo = new CancellationTokenSource();
CancellationTokenSource ido = idoMp > 0 ? new CancellationTokenSource(TimeSpan.FromSeconds(idoMp)) : new CancellationTokenSource();
CancellationTokenSource egyutt = CancellationTokenSource.CreateLinkedTokenSource(felhasznalo.Token, ido.Token);
forras = felhasznalo;
Eredmeny er = new Eredmeny();
string uzenet;
ora.Restart();
tmrKijelzo.Start();
Beallit(Allapot.Fut);
try
{
CancellationToken token = egyutt.Token;
await Task.Run(() => Dolgoz(mappa, alkonyvtar, er, token), token);
uzenet = "Kész: " + er.Fajlok + " fájl, " + Meret(er.OsszMeret) + ", " + ora.Elapsed.TotalSeconds.ToString("0.0") + " mp.";
}
catch (OperationCanceledException)
{
uzenet = (ido.IsCancellationRequested ? "Időtúllépés (" + idoMp + " mp)" : "Megszakítva") + ": " + er.Fajlok + " fájl feldolgozva.";
}
catch (Exception ex)
{
uzenet = "Hiba: " + ex.Message;
}
finally
{
tmrKijelzo.Stop();
ora.Stop();
egyutt.Dispose(); ido.Dispose(); felhasznalo.Dispose();
forras = null;
}
if (IsDisposed) { return; }
Kijelez(er); // a háttérszál befejezte: az Eredmeny biztonságosan olvasható
pb.MarqueeAnimationSpeed = 0;
pb.Style = ProgressBarStyle.Continuous;
long ossz = Interlocked.Read(ref osszes);
pb.Maximum = (int)Math.Max(1, Math.Min(int.MaxValue, ossz));
pb.Value = (int)Math.Min(pb.Maximum, Interlocked.Read(ref feldolgozott));
lblAllapot.Text = uzenet;
tsslUzenet.Text = uzenet;
lblMostani.Text = "";
fazis = 0;
Beallit(Allapot.Kesz);
}
// ---- a háttérmunka: vezérlőt NEM ér ----
private void Dolgoz(string mappa, bool alkonyvtar, Eredmeny er, CancellationToken token)
{
List<string> fajlok = new List<string>();
Stack<string> verem = new Stack<string>();
verem.Push(mappa);
// 1. fázis: bejárás
while (verem.Count > 0)
{
token.ThrowIfCancellationRequested();
szunet.Wait(token);
string m = verem.Pop();
aktualis = m;
try
{
foreach (string f in Directory.EnumerateFiles(m)) { fajlok.Add(f); Interlocked.Increment(ref szamolt); }
if (alkonyvtar)
{
foreach (string d in Directory.EnumerateDirectories(m))
{
if ((File.GetAttributes(d) & FileAttributes.ReparsePoint) == 0) { verem.Push(d); } // hivatkozást nem követünk
}
}
er.Mappak++;
}
catch (UnauthorizedAccessException) { er.Hibak++; er.HibaSzovegek.Add(m + ": nincs hozzáférés"); }
catch (IOException ex) { er.Hibak++; er.HibaSzovegek.Add(m + ": " + ex.Message); }
}
Interlocked.Exchange(ref osszes, fajlok.Count);
fazis = 2;
// 2. fázis: feldolgozás
foreach (string f in fajlok)
{
token.ThrowIfCancellationRequested();
szunet.Wait(token); // szünetben itt áll; a megszakítás kiszabadítja
aktualis = f;
try
{
long meret = new FileInfo(f).Length;
string kit = Path.GetExtension(f).ToLowerInvariant();
if (kit.Length == 0) { kit = "(nincs)"; }
KiterjStat ks;
if (!er.Kiterj.TryGetValue(kit, out ks)) { ks = new KiterjStat(); er.Kiterj[kit] = ks; }
ks.Db++;
ks.Meret += meret;
er.Fajlok++;
er.OsszMeret += meret;
Interlocked.Add(ref osszMeret, meret);
er.Legnagyobbak.Add(new KeyValuePair<string, long>(f, meret));
if (er.Legnagyobbak.Count > 20)
{
er.Legnagyobbak.Sort((a, b) => b.Value.CompareTo(a.Value));
er.Legnagyobbak.RemoveRange(10, er.Legnagyobbak.Count - 10);
}
}
catch (IOException ex) { er.Hibak++; er.HibaSzovegek.Add(Path.GetFileName(f) + ": " + ex.Message); }
catch (UnauthorizedAccessException) { er.Hibak++; er.HibaSzovegek.Add(Path.GetFileName(f) + ": nincs hozzáférés"); }
Interlocked.Increment(ref feldolgozott);
}
}
// ---- a felület frissítése: 200 ms-onként, a felületi szálon ----
private void tmrKijelzo_Tick(object sender, EventArgs e)
{
lblIdo.Text = ora.Elapsed.ToString(@"mm\:ss");
string ak = aktualis;
lblMostani.Text = ak.Length > 70 ? "..." + ak.Substring(ak.Length - 67) : ak;
if (fazis == 1)
{
lblSzazalek.Text = Interlocked.Read(ref szamolt) + " fájl eddig";
}
else if (fazis == 2)
{
long k = Interlocked.Read(ref feldolgozott), o = Interlocked.Read(ref osszes);
if (pb.Style != ProgressBarStyle.Continuous)
{
pb.MarqueeAnimationSpeed = 0;
pb.Style = ProgressBarStyle.Continuous;
pb.Minimum = 0;
pb.Maximum = (int)Math.Max(1, Math.Min(int.MaxValue, o));
lblAllapot.Text = allapot == Allapot.Szunet ? "Szünet" : "Feldolgozás...";
}
pb.Value = (int)Math.Min(pb.Maximum, k);
double arany = o == 0 ? 1 : (double)k / o;
string szoveg = k + " / " + o + " (" + arany.ToString("P0") + ")";
if (arany >= 0.05 && ora.Elapsed.TotalSeconds >= 3 && k < o)
{
szoveg += ", még kb. " + Hatralevo(TimeSpan.FromSeconds(ora.Elapsed.TotalSeconds / arany - ora.Elapsed.TotalSeconds));
}
lblSzazalek.Text = szoveg;
}
}
private string Hatralevo(TimeSpan t)
{
if (t.TotalSeconds < 5) { return "néhány másodperc"; }
if (t.TotalSeconds < 90) { return Math.Round(t.TotalSeconds / 5) * 5 + " mp"; }
return Math.Round(t.TotalMinutes) + " perc";
}
private string Meret(long b)
{
string[] egyseg = { "B", "KB", "MB", "GB", "TB" };
double v = b;
int i = 0;
while (v >= 1024 && i < egyseg.Length - 1) { v /= 1024; i++; }
return v.ToString(i == 0 ? "0" : "0.##") + " " + egyseg[i];
}
private void Kijelez(Eredmeny er)
{
er.Legnagyobbak.Sort((a, b) => b.Value.CompareTo(a.Value));
foreach (KeyValuePair<string, long> p in er.Legnagyobbak.Take(10)) { lstLegnagyobb.Items.Add(Meret(p.Value) + " " + p.Key); }
foreach (KeyValuePair<string, KiterjStat> p in er.Kiterj.OrderByDescending(x => x.Value.Meret))
{
lstKiterjesztes.Items.Add(p.Key + ": " + p.Value.Db + " db, " + Meret(p.Value.Meret));
}
lstNaplo.Items.Add("Mappák: " + er.Mappak + ", fájlok: " + er.Fajlok + ", összméret: " + Meret(er.OsszMeret) + ", hibák: " + er.Hibak);
foreach (string h in er.HibaSzovegek.Take(200)) { lstNaplo.Items.Add("⚠ " + h); }
}
// ---- szünet, megszakítás, bezárás ----
private void btnSzunet_Click(object sender, EventArgs e)
{
if (allapot == Allapot.Fut)
{
szunet.Reset();
ora.Stop();
Beallit(Allapot.Szunet);
lblAllapot.Text = "Szünet";
}
else if (allapot == Allapot.Szunet)
{
szunet.Set();
ora.Start();
Beallit(Allapot.Fut);
lblAllapot.Text = fazis == 1 ? "Fájlok keresése..." : "Feldolgozás...";
}
}
private void btnMegse_Click(object sender, EventArgs e)
{
Beallit(Allapot.Megszakitas);
if (forras != null) { forras.Cancel(); }
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Escape && btnMegse.Enabled) { btnMegse.PerformClick(); return true; }
return base.ProcessCmdKey(ref msg, keyData);
}
private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (forras != null && !bezarasra)
{
e.Cancel = true;
bezarasra = true;
forras.Cancel();
while (forras != null) { await Task.Delay(50); }
Close();
}
}
Tesztlista (18 pont)
Minden pontnál próbáld ki, és jegyezd fel az eredményt.
| # | Teszt | Elvárt eredmény |
|---|---|---|
| 1 | Kis mappa (10 fájl) elemzése | Gyors befejezés, helyes darabszám és összméret. |
| 2 | Nagy mappafa (több ezer fájl) | A felület közben él (az ablak mozgatható), a jelző halad. |
| 3 | 1. fázis kijelzése | Marquee sáv és a talált fájlok növekvő száma. |
| 4 | 2. fázis kijelzése | Százalékos jelző, k / n (p%), aktuális fájl. |
| 5 | Hátralévő idő | 5% és 3 mp előtt nincs; utána értelmes, nem ugráló becslés. |
| 6 | Szünet | A feldolgozás és az eltelt idő megáll; a felület él. |
| 7 | Folytatás | Onnan folytatódik; az idő is. |
| 8 | Mégse az 1. fázisban | Leáll, Megszakítva, a gombok helyesek. |
| 9 | Mégse a 2. fázisban | Részeredmény látszik, nincs hibaüzenet. |
| 10 | Mégse szünetben | A megszakítás szünetben is hat. |
| 11 | Újraindítás megszakítás után | Új forrás, nulláról indul, helyes számok. |
| 12 | Időtúllépés (pl. 2 mp) | Megáll, az üzenet Időtúllépés, részeredménnyel. |
| 13 | Hozzáférés nélküli mappa (pl. rendszermappa) | Kihagyva, a naplóban szerepel, a feldolgozás folytatódik. |
| 14 | Nem létező mappa | Érthető üzenet, nincs kivétel. |
| 15 | Üres mappa | 0 fájl, nincs ArgumentOutOfRangeException, Kész. |
| 16 | Bezárás futás közben | Megszakít, megvár, bezár; nincs kivétel, nincs szellemfolyamat. |
| 17 | Dupla kattintás az Indítás és a Mégse gombon | Nem indul két feldolgozás; nincs ObjectDisposedException. |
| 18 | Akadálymentesség | Gyorsbillentyűk működnek, az állapot szövegesen is látszik, a gombok állapota helyes. |
Gyakori hibák és önértékelés
Gyakori hibák
- A háttérszál közvetlenül írja a vezérlőket (cross-thread kivétel).
- Üres mappánál a
pb.Maximum = 0ésValue = 1. - A
Marqueesáv a végén tovább fut. - A megszakítás hibaüzenetként jelenik meg.
- A régi, megszakított forrás újrahasználata.
- A hivatkozások követése végtelen ciklust okoz.
- A felületet minden fájl után frissíted (túlterhelés).
Önértékelés
Pontozd magad a követelménytábla szerint, majd válaszolj:
- Hol voltál bizonytalan:
await,Interlocked,ManualResetEventSlimvagy a token? - Mit tennél másképp, ha újra kezdenéd?
- Miért jobb az időzítős frissítés, mint a soronkénti
BeginInvoke? - Mi történik, ha a felhasználó szünetben bezárja az ablakot?
Bővítési ötletek
- Párhuzamos feldolgozás: a fájlok méretének lekérdezése
Parallel.ForEachmellett (Interlocked, szálanként külön szótár a végén egyesítve), és a gyorsulás mérése. - Szűrők: kiterjesztés, minimális méret, módosítási dátum szerinti szűrés.
- Exportálás: az eredmény mentése CSV-be vagy JSON-ba (10.5–10.6 lecke), mentési hibakezeléssel.
- Beállítások: az utolsó mappa, az ablak mérete és a beállítások tárolása (10.8 lecke).
- Tálca-folyamatjelző: az ablakcímben a százalék (Mappa-elemző – 42%).
- Fa nézet: a mappák méretének hierarchikus kimutatása (
TreeView), a legnagyobb mappák keresése.
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. Emberi méret
Írj metódust, amely a bájtokat KB, MB, GB egységben adja vissza. Ciklusban 1024-gyel osztod, amíg az érték 1024 alá nem esik; az egység tömb indexe nő. Lásd a mintamegoldás Megoldás megnyitása
Meret metódusát.
2. Két fázis
Miért két fázisú a folyamatjelzés? Az első fázisban az összes fájl száma még ismeretlen (Megoldás megnyitása
Marquee), a másodikban már ismert, ezért százalékos jelző és becsült idő adható.
Közepes szint
3. Szünet megszakításkor
Szünetben a háttérszál a A Wait(token) hívásban áll. Mi történik a Mégse után?Megoldás megnyitása
Wait(token) a megszakításkor OperationCanceledException kivételt dob, így a szünetben álló szál is kiszabadul és kilép.
4. Mozgóátlag
Hogyan javítanád a becsült hátralévő idő pontosságát? Az utolsó 5–10 másodperc sebességéből számolsz (a 11.2 lecke mozgóátlaga), így az eltérő méretű fájlok kevésbé zavarják.Megoldás megnyitása
Kihívás szint
5. Párhuzamos méretlekérdezés
Hogyan gyorsítanád a 2. fázist több szállal, és mi a kockázat?Megoldás megnyitása
Parallel.ForEach opciókkal (CancellationToken, MaxDegreeOfParallelism), a számlálók Interlocked-dal, a szótár lock alatt vagy szálanként külön gyűjtve. Kockázat: a lemez (főleg HDD) nem gyorsul a több szállal; a szünet és a megszakítás kezelése összetettebb.
6. Egységteszt
Hogyan tennéd tesztelhetővé a Önálló osztályba teszed (nincs vezérlő a kódban), az állapotot Dolgoz logikát felület nélkül?Megoldás megnyitása
IProgress<T> vagy események jelzik; a teszt ideiglenes mappafával futtatja, és az Eredmeny értékeit ellenőrzi.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. A vezérlők csak a felületi szálról érhetők el; a háttérszál az adatot közzéteszi, a felület időzítővel olvassa. Közös számlálók szálbiztos módosítására. Az időzítős forrás A A megszakított forrás végleg megszakított marad. Megszakítod a munkát, 1. Miért nem ír a háttérszál vezérlőt?
2. Mire jó az Interlocked?
3. Hogyan működik a szünet?
ManualResetEventSlim: a munka a Wait(token) hívásban vár, amíg a Set() el nem engedi.4. Hogyan különböztetjük meg az időtúllépést a Mégse-től?
IsCancellationRequested tulajdonságából.5. Mit teszel üres mappánál?
Maximum legalább 1; érthető üzenet; nincs kivétel.6. Miért új forrás minden indításhoz?
7. Mit teszel bezáráskor?
e.Cancel = true mellett megvárod, majd bezársz.
A 11. fejezet összefoglalása: mit tudsz már?
- Időzítőt használsz, és az időt
Stopwatch-csal méred. - Folyamatot jelzel (határozott és határozatlan), becsült idővel.
- Érted a felületi szálat, a blokkolást és a holtpontot.
async/await,Task.Run,Parallelsegítségével háttérben dolgozol.- Biztonságosan frissíted a felületet, és védetten kezeled a közös adatokat.
- Megszakítható, szüneteltethető, időkorlátos műveleteket írsz.
Következő fejezet: OOP és programtervezés
A 12. fejezetben rendezett, újrahasználható szerkezetet adsz a programoknak: osztályok, felelősségek, rétegek.

