Grafikus C# – Windows Forms tanfolyam › 11.1 lecke Windows Forms · 11.1 lecke · időzítők Az 1.8 leckében a Timer és Tick esemény
Timer alapjait megismerted, és az 5.9 leckében a kígyójáték is rá épült. Most mélyebbre nézünk, mert az időzítőnek van néhány alattomos tulajdonsága: a Tick nem pontos, a Tick-ek számolásával mért idő elcsúszik, és a .NET-ben háromféle Timer osztály van, amelyek más szálon futnak. Megtanulod, mikor melyiket válaszd, hogyan mérsz pontos időt a Stopwatch-csal, hogyan készítesz visszaszámlálót szünettel, és hogyan szabadítod fel az időzítőt. Ez a lecke a fejezet többi részének alapja: az időzítő a legegyszerűbb módja annak, hogy a program „közben is éljen”.
A lecke végére
- megkülönbözteted a három
Timerosztályt (Windows.Forms,Timers,Threading), és tudod, melyik fut a felület szálán; - érted, miért nem pontos a
Tick(~15 ms felbontás, a felület szál terhelése), és hogy a Tick-számlálás elcsúszik; - az időt a
Stopwatchvagy az indulás időpontjából számolod, nem a Tick-ekből; - visszaszámlálót készítesz szünettel és folytatással;
- helyesen indítod, leállítod és felszabadítod az időzítőt (
Start,Stop,Enabled,Dispose); - elkerülöd a tipikus hibákat (hosszú
Tickkezelő, párhuzamosElapsed, felszabadítatlan időzítő).
Mielőtt elkezded
Ehhez a leckéhez az 1.8 lecke (Timer), az 5.1 lecke (események) és a 9.8 lecke (Dispose, using) szükséges.
Háromféle Timer: melyik, mikor?
| Osztály | Esemény | Melyik szálon? | Mikor? |
|---|---|---|---|
System.Windows.Forms.Timer | Tick | A felület szálán | Felületet frissítő időzítés (óra, animáció, automatikus mentés). A Toolboxban ez van. |
System.Timers.Timer | Elapsed | Szálkészlet (thread pool); a SynchronizingObject beállításával a felület szálán | Szerver- és háttérfeladatok; felületi frissítéshez csak SynchronizingObject = this vagy Invoke mellett. |
System.Threading.Timer | visszahívás (TimerCallback) | Szálkészlet | Könnyű, kód-alapú háttér időzítés; nincs vizuális tervezője. |
// 1. a Toolbox Timer: a Tick a FELÜLET szálán fut, ezért a vezérlők közvetlenül elérhetők
private void tmrOra_Tick(object sender, EventArgs e)
{
lblIdo.Text = DateTime.Now.ToString("HH:mm:ss"); // rendben
}
// 2. System.Timers.Timer: az Elapsed a szálkészleten fut: a vezérlő elérése hibát okoz!
System.Timers.Timer hatterIdo = new System.Timers.Timer(1000);
hatterIdo.Elapsed += (s, e) =>
{
// lblIdo.Text = ...; // InvalidOperationException: másik szálról nem érhető el a vezérlő (11.3 és 11.7 lecke)
this.BeginInvoke(new Action(() => lblIdo.Text = DateTime.Now.ToString("HH:mm:ss"))); // a felületi szálra küldjük
};
hatterIdo.Start();
// vagy: az Elapsed a felület szálán fusson (a Form a SynchronizingObject)
hatterIdo.SynchronizingObject = this;Windows.Forms.Timer: a Tick a felület szálán fut, nincs szálkezelési gond. A másik kettőt a háttérmunkához használjuk (11.6 lecke).using System.Windows.Forms; és a using System.Timers; együtt használva a Timer név kétértelmű. Írd ki a teljes nevet (System.Windows.Forms.Timer), vagy ne tedd be a System.Timers névteret.Windows.Forms.Timer.Tick a felület szálán fut: ha a kezelő lassú, a felület megfagy (11.3 lecke). Hosszú számítást a Tick-ben ne végezz.Windows.Forms.Timer a Windows üzenetsorán át kap jelzést: ha a felület szál foglalt, a Tick-ek késnek, és összevonódnak (nem torlódnak fel).Jósolj!
Egy System.Timers.Timer Elapsed eseménye közvetlenül a lblIdo.Text értéket állítja be. Mi történik?
Jóslat ellenőrzése
InvalidOperationException (Cross-thread operation not valid) (vagy a program csendben hibásan viselkedik, főleg Release módban): az Elapsed a szálkészleten fut, és a vezérlőt csak a létrehozó (felületi) szál érheti el. A megoldás a BeginInvoke, a SynchronizingObject = this, vagy egyszerűen a Windows.Forms.Timer.
Pontosság: miért nem szabad a Tick-eket számolni?
A Windows.Forms.Timer felbontása kb. 15–16 ezredmásodperc, és a Tick késhet, ha a felület szál foglalt. Ha a program minden Tick-nél „eltelt egy másodperc”-et feltételez, az idő elcsúszik. A helyes módszer: indításkor megjegyzed az időt, és minden Tick-nél a tényleges eltelt időt olvasod le a Stopwatch-csal:
// HIBÁS: a Tick-ek számolása; minden késett Tick elvész, az óra lassul
private int masodperc = 0;
private void tmrRossz_Tick(object sender, EventArgs e)
{
masodperc++; // feltételezi, hogy pontosan 1000 ms telt el
lblRossz.Text = TimeSpan.FromSeconds(masodperc).ToString(@"mm\:ss");
}
// HELYES: a tényleges eltelt idő a Stopwatch-ból
private Stopwatch ora = Stopwatch.StartNew();
private void tmrJo_Tick(object sender, EventArgs e)
{
lblJo.Text = ora.Elapsed.ToString(@"mm\:ss\.f"); // akkor is pontos, ha a Tick késik
}
// a Tick gyakorisága és az idő pontossága két külön kérdés:
// a Tick csak a KIJELZÉS frissítésének üteme (200 ms elég), a pontos időt a Stopwatch tartja
tmrJo.Interval = 200;Stopwatch a rendszer nagy felbontású számlálóját használja (mikroszekundum pontosságú), nem függ a felület szál terhelésétől. Az Elapsed az indítás óta eltelt TimeSpan; a Start, Stop (szünet), Reset, Restart metódusokkal vezérelhető.Tick gyakorisága (Interval) csak azt mondja meg, milyen gyakran frissül a kijelző. Ha 200 ms-onként frissítesz, a másodperc váltása is legfeljebb 200 ms késéssel látszik, de az érték mindig pontos.DateTime.Now) is megjegyezheted, és DateTime.Now - indulas módon számolhatsz. A Stopwatch pontosabb és nem függ a rendszerórától (óraátállítás, téli-nyári időszámítás).Interval ezredmásodpercben van, értéke legalább 1; a gyakorlati minimum kb. 15 ms. 1 ms-os időzítés a felületen nem megbízható, és fölöslegesen terheli a felület szálat.Jósolj!
A program tmrRossz (Interval = 1000) mellett a masodperc++ alapján számol, miközben egy másik gomb 5 másodpercig dolgoztatja a felület szálát. Mit mutat az óra 10 másodperc valódi idő után?
Jóslat ellenőrzése
Kb. 5–6 másodpercet: a felület szál foglalt volt, a késett Tick-ek összevonódtak (nem pótlódtak), így a számláló lemaradt. A Stopwatch alapján mért idő 10 másodpercet mutat, függetlenül a Tick-ektől.
Visszaszámláló szünettel
private Stopwatch ora = new Stopwatch();
private TimeSpan osszes = TimeSpan.FromMinutes(5);
private void btnStart_Click(object sender, EventArgs e)
{
if (!ora.IsRunning)
{
if (ora.Elapsed >= osszes) { ora.Reset(); } // lejárt után újraindítás
ora.Start();
tmr.Interval = 200;
tmr.Start();
}
}
private void btnSzunet_Click(object sender, EventArgs e)
{
ora.Stop(); // a Stopwatch megáll, az Elapsed megmarad; a Start folytatja
tmr.Stop();
}
private void btnNulla_Click(object sender, EventArgs e)
{
ora.Reset();
tmr.Stop();
Kijelez();
}
private void tmr_Tick(object sender, EventArgs e)
{
Kijelez();
if (ora.Elapsed >= osszes)
{
ora.Stop();
tmr.Stop();
SystemSounds.Exclamation.Play();
lblAllapot.Text = "Lejárt az idő!"; // hang mellé szöveg is kell: a hang nem mindenkinek hallható (6.9 lecke)
}
}
private void Kijelez()
{
TimeSpan hatra = osszes - ora.Elapsed;
if (hatra < TimeSpan.Zero) { hatra = TimeSpan.Zero; }
// felfelé kerekítve: 4:59,3 → 05:00 helyett 04:59; az utolsó 0,9 mp is „1”-ként látszik
int mp = (int)Math.Ceiling(hatra.TotalSeconds);
lblIdo.Text = (mp / 60).ToString("00") + ":" + (mp % 60).ToString("00");
pb.Value = (int)(ora.Elapsed.TotalSeconds * 100 / osszes.TotalSeconds > 100 ? 100 : ora.Elapsed.TotalSeconds * 100 / osszes.TotalSeconds);
}Stopwatch.Stop() megőrzi az eltelt időt, a Start() onnan folytatja. A Reset() nullázza. Ha a Tick számolna, a szünet kezelése sokkal bonyolultabb lenne.Ceiling): a „00:00” csak akkor látszik, amikor tényleg lejárt az idő. Lefelé kerekítve a 00:00 az utolsó másodpercben látszana.Tick-et, de egy már a sorban álló még megérkezhet. A Tick-ben ezért érdemes ellenőrizni az állapotot (ora.IsRunning).Jósolj!
A visszaszámláló (int)hatra.TotalSeconds módon (lefelé kerekítve) mutatja a hátralévő időt egy 10 másodperces visszaszámlálón. Mikor jelenik meg először a 00?
Jóslat ellenőrzése
Már az utolsó másodpercben (9 másodperc után): a 0,9 másodperc hátralévő idő lefelé kerekítve 0. A felhasználó azt látja, hogy „00”, pedig még majdnem egy másodperc van hátra. A Math.Ceiling ezt helyesen kezeli.
Az időzítő életciklusa
// Start() és Stop() = Enabled = true / false; az Interval módosítása működés közben is érvényes
tmr.Enabled = true;
tmr.Interval = 500;
// egyszeri késleltetés: a Tick-ben az első hívás után leállítjuk magát
private void tmrKesleltet_Tick(object sender, EventArgs e)
{
tmrKesleltet.Stop(); // csak egyszer fut
lblUzenet.Text = ""; // például: az állapotüzenet eltűnik 3 másodperc után
}
// a Designerben létrehozott Timer a komponenseket kezelő gyűjteménybe kerül: a Form zárásakor felszabadul.
// Kódból létrehozott időzítőt a Form zárásakor TE szabadítod fel:
private System.Windows.Forms.Timer villogo = new System.Windows.Forms.Timer { Interval = 500 };
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
villogo.Stop();
villogo.Dispose();
}Timer alapból ismétlődik. Ha egyszer akarod, a Tick első sora a Stop(). A felhasználói üzenet (Mentve) eltüntetésére ez gyakori minta.Timer a Form components gyűjteményébe kerül, és a Form Dispose metódusa felszabadítja. Kódból (new Timer) létrehozott példányt neked kell.Tick-et (ha nincs leállítva): a kezelő felszabadított vezérlőket érhet el. Zárás előtt a Stop().Jósolj!
A program for ciklusban hívja a tmr.Start() metódust 100-szor. Hány Tick keletkezik intervallumonként?
Jóslat ellenőrzése
Egy: az időzítő egyetlen példány, a többszöri Start() nem hoz létre újabbakat (az Enabled már igaz, a hívás nem hatásos, vagy újraindítja a számlálást). Új időzítőt csak new Timer() hoz létre: ezeket külön fel kell szabadítani.
Mit tegyél ide: az időmérő-labor: a Tick-számláló és a Stopwatch órája, az elcsúszás, és a visszaszámláló.Javasolt fájlnév: winforms-11-1-idomero.webp · Alt szöveg: Az időmérő-labor 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. Az óra elcsúszik
masodperc++; // Tick-enként 1Hiba és javítás
A késett Tick-ek elvesznek. Használj Stopwatch vagy indulási időpont alapú számítást.
2. Cross-thread kivétel az Elapsed-ben
t.Elapsed += (s, e) => lblIdo.Text = "..."; // System.Timers.TimerHiba és javítás
Az Elapsed a szálkészleten fut. Használj Windows.Forms.Timer-t, vagy BeginInvoke / SynchronizingObject.
3. A program lefagy a Tick-ben
private void tmr_Tick(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(3000); // a felület szálát blokkolja
}Hiba és javítás
A Tick a felület szálán fut: a hosszú munka megfagyasztja az ablakot. Gyors kezelő kell; a hosszú munka a háttérbe (11.5–11.6 lecke).
4. A Tick bezárás után is fut
A Form bezárása után kivétel jelenik meg a Tick kezelőben. Az időzítő nincs leállítva: a Ok és javítás
FormClosing vagy FormClosed eseményben Stop() (és kódból létrehozottnál Dispose()).
5. Az időzítő nem indul
tmr.Interval = 1000; // Start() nélkül; az Enabled alapból hamisHiba és javítás
A Timer alapból le van állítva. Hívd a Start() metódust, vagy állítsd az Enabled tulajdonságot true értékre (a Designerben is).
6. A lejárat 1 másodperccel korábban látszik
A visszaszámláló az utolsó másodperc előtt 00-t mutat. Lefelé kerekítés (Ok és javítás
(int)TotalSeconds). Használj Math.Ceiling értéket.
Mini projekt: időmérő-labor
Készíts programot, amely az időzítők pontosságát és használatát mutatja:
- Felület: három panel (
TabControlfülei): Pontosság, Visszaszámláló, Gyakorlás. - Pontosság fül: két óra egymás mellett: a Tick-számláló (
masodperc++) és a Stopwatch óra, mindkettőInterval = 1000; Terhelés (3 mp) gomb, amely a felület szálát 3 másodpercig foglalja (Thread.Sleep), utána az eltérés látszik (lblElteres: A Tick-óra 2,9 másodpercet késik); a Visszaállítás nullázza mindkettőt. - Visszaszámláló fül: a beállítható idő (
nudPerc,nudMasodperc), Start, Szünet, Folytat (ugyanaz a Start), Nulla; aProgressBara hátralévő arányt mutatja;Ceilingkerekítés; a lejáratkor hang és szöveg (Lejárt az idő!), a villogás legfeljebb 2 Hz. - Gyakorlás fül: az Egyszeri üzenet: a gombra kattintva az állapotsor Mentve üzenete 3 másodperc múlva eltűnik (egyszeri időzítő); a Háttér időzítő (System.Timers) jelölő: az óra az
Elapsed-ből frissülBeginInvokesegítségével, majd (kísérletként) közvetlen vezérlő-eléréssel (a kivétel az állapotsorba kerül, nem omlik össze). - Életciklus: minden időzítő leáll a
FormClosing-ben; a kódból létrehozott felszabadul. - Terhelés a felületen: az Interval csúszka (
TrackBar, 15–1000 ms) a Tick-gyakoriságot állítja; a kijelzés aStopwatchalapján mindig pontos; alblTickDba másodpercenkénti Tick-ek számát mutatja. - Teszteld: a terhelés előtt és után az eltérés, szünet és folytatás a visszaszámlálón, 0 másodperces beállítás, a Form bezárása futó időzítővel, a minimális intervallum, a
Cross-threadkísérlet.
Megoldás megnyitása (próbáld ki előbb önállóan!)
A központi rész a Tick-számlálás és a Stopwatch összevetése, és a szünetet kezelő visszaszámláló.
private int tickMasodperc = 0;
private Stopwatch pontosOra = Stopwatch.StartNew();
private int tickDb = 0;
private DateTime tickMeres = DateTime.Now;
private Stopwatch vissza = new Stopwatch();
private TimeSpan osszes = TimeSpan.FromMinutes(1);
private System.Timers.Timer hatterIdo;
private void Form1_Load(object sender, EventArgs e)
{
tmrPontossag.Interval = 1000;
tmrPontossag.Start();
tmrVissza.Interval = 200;
tbInterval.Minimum = 15;
tbInterval.Maximum = 1000;
tbInterval.Value = 200;
}
private void tmrPontossag_Tick(object sender, EventArgs e)
{
tickMasodperc++;
lblTick.Text = TimeSpan.FromSeconds(tickMasodperc).ToString(@"mm\:ss");
lblPontos.Text = pontosOra.Elapsed.ToString(@"mm\:ss\.f");
double eltérés = pontosOra.Elapsed.TotalSeconds - tickMasodperc;
lblElteres.Text = Math.Abs(eltérés) < 0.5 ? "A két óra egyezik." : "A Tick-óra " + eltérés.ToString("0.0") + " másodpercet késik.";
}
private void btnTerheles_Click(object sender, EventArgs e)
{
Thread.Sleep(3000); // szándékosan: a felület szálát blokkolja (a 11.4 lecke témája)
}
private void btnVisszaallit_Click(object sender, EventArgs e)
{
tickMasodperc = 0;
pontosOra.Restart();
}
// ---- visszaszámláló ----
private void btnStart_Click(object sender, EventArgs e)
{
if (vissza.IsRunning) { return; }
if (vissza.Elapsed == TimeSpan.Zero || vissza.Elapsed >= osszes)
{
osszes = TimeSpan.FromSeconds((int)nudPerc.Value * 60 + (int)nudMasodperc.Value);
if (osszes <= TimeSpan.Zero) { lblAllapot.Text = "Adj meg legalább 1 másodpercet."; return; }
vissza.Reset();
}
vissza.Start();
tmrVissza.Start();
lblAllapot.Text = "Fut...";
}
private void btnSzunet_Click(object sender, EventArgs e)
{
vissza.Stop();
tmrVissza.Stop();
lblAllapot.Text = "Szünet";
}
private void btnNulla_Click(object sender, EventArgs e)
{
vissza.Reset();
tmrVissza.Stop();
Kijelez();
lblAllapot.Text = "";
}
private void tmrVissza_Tick(object sender, EventArgs e)
{
Kijelez();
if (vissza.IsRunning && vissza.Elapsed >= osszes)
{
vissza.Stop();
tmrVissza.Stop();
SystemSounds.Exclamation.Play();
lblAllapot.Text = "Lejárt az idő!"; // hang MELLETT látható szöveg
}
}
private void Kijelez()
{
TimeSpan hatra = osszes - vissza.Elapsed;
if (hatra < TimeSpan.Zero) { hatra = TimeSpan.Zero; }
int mp = (int)Math.Ceiling(hatra.TotalSeconds);
lblIdo.Text = (mp / 60).ToString("00") + ":" + (mp % 60).ToString("00");
pbVissza.Value = osszes.TotalSeconds <= 0 ? 0 : Math.Min(100, (int)(hatra.TotalSeconds * 100 / osszes.TotalSeconds));
}
// ---- egyszeri üzenet ----
private void btnMentve_Click(object sender, EventArgs e)
{
tsslUzenet.Text = "Mentve";
tmrUzenet.Stop();
tmrUzenet.Interval = 3000;
tmrUzenet.Start(); // újraindul, ha már futott
}
private void tmrUzenet_Tick(object sender, EventArgs e)
{
tmrUzenet.Stop(); // egyszeri
tsslUzenet.Text = "";
}
// ---- System.Timers kísérlet ----
private void chkHatter_CheckedChanged(object sender, EventArgs e)
{
if (chkHatter.Checked)
{
hatterIdo = new System.Timers.Timer(1000);
hatterIdo.Elapsed += (s, ev) =>
{
try { this.BeginInvoke(new Action(() => lblHatter.Text = DateTime.Now.ToString("HH:mm:ss"))); }
catch (ObjectDisposedException) { } // a Form közben bezárult
catch (InvalidOperationException) { }
};
hatterIdo.Start();
}
else if (hatterIdo != null)
{
hatterIdo.Stop();
hatterIdo.Dispose();
hatterIdo = null;
}
}
private void btnKiserlet_Click(object sender, EventArgs e)
{
System.Timers.Timer t = new System.Timers.Timer(100) { AutoReset = false };
t.Elapsed += (s, ev) =>
{
try { lblHatter.Text = "közvetlenül"; } // másik szálról: InvalidOperationException
catch (InvalidOperationException ex) { this.BeginInvoke(new Action(() => tsslUzenet.Text = "Cross-thread kivétel: " + ex.Message)); }
finally { t.Dispose(); }
};
t.Start();
}
// ---- a Tick gyakorisága ----
private void tbInterval_Scroll(object sender, EventArgs e)
{
tmrGyakori.Interval = tbInterval.Value;
tmrGyakori.Start();
}
private void tmrGyakori_Tick(object sender, EventArgs e)
{
tickDb++;
lblPontos2.Text = pontosOra.Elapsed.ToString(@"mm\:ss\.fff"); // a kijelzés sűrű, az érték pontos
if ((DateTime.Now - tickMeres).TotalSeconds >= 1)
{
lblTickDb.Text = tickDb + " Tick / mp";
tickDb = 0;
tickMeres = DateTime.Now;
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
tmrPontossag.Stop();
tmrVissza.Stop();
tmrGyakori.Stop();
tmrUzenet.Stop();
if (hatterIdo != null) { hatterIdo.Stop(); hatterIdo.Dispose(); }
}Mit tegyél ide: a kész időmérő-labor három füle.Javasolt fájlnév: winforms-11-1-kesz.webp · Alt szöveg: A kész időmérő-labor
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. Stopper köridőkkel
Készíts stoppert Kör gombbal, amely a köridőket egy listába teszi. Mit jegyzel meg a körökhöz? A Egy lehetséges megoldás
Stopwatch.Elapsed értékét kör közben, és az előző kör értékét kivonva kapod a köridőt.private TimeSpan utolsoKor = TimeSpan.Zero;private void btnKor_Click(object sender, EventArgs e){ TimeSpan most = ora.Elapsed; lstKorok.Items.Add("Kör " + (lstKorok.Items.Count + 1) + ": " + (most - utolsoKor).ToString(@"mm\:ss\.ff") + " (összesen " + most.ToString(@"mm\:ss\.ff") + ")"); utolsoKor = most;}
2. Villogó figyelmeztetés
Egy címke másodpercenként kétszer vált színt (piros/fehér). Mi a felső határ az akadálymentesség szempontjából? Legfeljebb 3 villanás másodpercenként (6.9 lecke); a 2 Hz biztonságos. A szöveg tartalma ne csak színnel jelezzen. A felhasználó kikapcsolhatja a villogást.Egy lehetséges megoldás
3. Automatikus kijelentkezés
A program 5 perc tétlenség után zárol. Hogyan méred a tétlenséget? Az utolsó felhasználói művelet időpontját (billentyű, egér események) egy mezőben tartod; egy 1 másodperces Egy lehetséges megoldás
Timer ellenőrzi: DateTime.Now - utolsoMuvelet > TimeSpan.FromMinutes(5). Nem számolod a Tick-eket.
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. Melyik Timer?
Melyiket használod: (a) a felületen frissülő óra; (b) háttérben futó, szálkészletes időzítés? (a) Megoldás megnyitása
System.Windows.Forms.Timer; (b) System.Timers.Timer vagy System.Threading.Timer.
2. Interval
Mekkora az Ezredmásodperc; a felületi időzítő felbontása kb. 15–16 ms.Interval egysége, és mi a gyakorlati alsó határ?Megoldás megnyitása
Közepes szint
3. Pontos idő kijelzése
Miért jó a 200 ms-os A kijelzés gyakran frissül (a másodperc váltása nem késik 0,2 másodpercnél többet), a pontos idő a Interval egy másodpercet kijelző órához?Megoldás megnyitása
Stopwatch-ból jön, és a Tick ritkán terheli a felületet.
4. Szünet és folytatás Stopwatch nélkül
Hogyan oldanád meg a szünetet, ha csak Az eltelt idő összegét (DateTime.Now alapon dolgoznál?Megoldás megnyitása
TimeSpan osszeg) és az utolsó indulás időpontját tartod; szünetnél hozzáadod az indulás óta eltelt időt; folytatásnál új időpontot jegyzel. A Stopwatch ezt egyszerűen megteszi.
Kihívás szint
5. Átfedő Elapsed
Egy Az System.Timers.Timer Elapsed kezelője 3 másodpercig fut, az Interval 1 másodperc. Mi történik?Megoldás megnyitása
Elapsed újra és újra elindul a szálkészleten, párhuzamosan (átfedő futások, versenyhelyzet). Védekezés: AutoReset = false és a végén újraindítás, vagy egy jelző (Interlocked.CompareExchange), amely kihagyja az átfedő hívásokat.
6. Időzítő a háttérszálon
Egy A visszahívás a szálkészleten fut: a vezérlő közvetlen eléréséhez System.Threading.Timer frissíti a felületet. Mi a hiba, és mi a jó megoldás?Megoldás megnyitása
Invoke / BeginInvoke kell. Egyszerűbb a Windows.Forms.Timer (a Tick a felület szálán fut); a Threading.Timer háttérmunkához való.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. A A felbontása kb. 15 ms, és a felület szál terhelése késleltetheti; a késett Tick-ek összevonódnak. Hogy a 00:00 csak a lejáratkor látszódjon. A Kódból létrehozott időzítőt a Form zárásakor (a Designerben felvettet a Form felszabadítja).1. Melyik Timer fut a felület szálán?
System.Windows.Forms.Timer (a Tick).2. Miért nem pontos a Tick?
3. Hogyan méred pontosan az időt?
Stopwatch vagy az indulás időpontja alapján, nem a Tick-ek számolásával.4. Hogyan kezeled a szünetet?
Stopwatch.Stop() és Start() (az Elapsed megmarad).5. Miért kerekítünk felfelé a visszaszámlálónál?
6. Mi az egyszeri időzítő mintája?
Tick első sora Stop().7. Mikor kell Dispose-olni az időzítőt?
Mit tudsz már?
- Megkülönbözteted a három
Timerosztályt, és tudod, melyik fut a felület szálán. - Pontos időt mérsz
Stopwatchsegítségével, a Tick-számlálás helyett. - Visszaszámlálót készítesz szünettel, helyes kerekítéssel és akadálymentes jelzéssel.
- Az időzítő életciklusát (indítás, leállítás, felszabadítás) helyesen kezeled.
- Elkerülöd a tipikus időzítő-hibákat.
Következő lecke: ProgressBar és állapotjelzés
A folyamatjelző vezérlők és az állapotjelzés tervezése: határozott és határozatlan folyamat, százalék, becsült hátralévő idő, akadálymentes tájékoztatás.



