Hogyan kell a timereket leállítani, illetve törlni a memóriából? (WPF)

Hogyan kell a timereket leállítani, illetve törlni a memóriából? (WPF)
2022-05-01T00:56:25+02:00
2022-05-03T23:15:13+02:00
2022-10-15T21:21:06+02:00
Papa76
A System.Windows.Threading.DispatcherTimer-t használom a Window-okban az adatok (pl.: Label) frissítésére. Viszont amikor a Window-ot bezárom this.Close() a timer továbbra is futtatja a Tick-ben meghatározott feladatot. Ezt "megoldottam???" a Closed eseménybe beírtam a Stop()-ot.
Aztán elkezdtem agyalni, és ebből lett a baj, amihez kérném a segítségeteket. Amikor egy új Window-ot megjelenítek, akkor az mindig egy új példány. Ráadásul abban a Timer is egy még újabb példány. Képes a program csak ezek alapján felszabadítani a feleslegessé váló memóriát, vagy valamit máshogy kellene megírnom, hogy ez valóban jó, és tartható legyen? Ugyanezt kérdezném a Window-al kapcsolatban is. A Close() felszabadítja a memóriát is? Köszi minden segítséget!
Mutasd a teljes hozzászólást!
Jó az irány, a Window.Closed eseményekor:
1) leállítod a DispatcherTimer-t,
2) leiratkozol a DispatcherTimer.Tick eseményről,
3) majd a GC felszabadítja a memóriát, amikor kedve tartja.
Az utolsó lépés feltétele, hogy ne legyen referenciád az adott Window példányra.

.NET-ben a memóriakezelés és -felszabadítás automatikusan történik (ezért hívják managed kódnak). Amikor lefut a garbage collector, végignézi a létrehozott objektumokat, és amelynek a reference counter-e 0-val egyenlő (tehát nincs semmilyen referencia az adott objektumra), felszabadítja azt, meghívva a destruktort.

Kézzel akkor "szabadítod fel" az objektumot, ha az implementálja az IDisposable interfészt. Ez azt jelenti, hogy az adott objektum a példányosításakor allokált valamilyen unmanaged resource-ot (pl. megnyitott egy fájlt, hálózati kommunikációs csatornát, operációs rendszer által biztosított erőforrást, stb.). Ekkor vagy egy using, vagy egy try-finally szerkezetet használva meghívod az IDisposable.Dispose() metódust, de ezzel csak az unmanaged resource-ot szabadítod fel (továbbá, használhatatlan állapotba állítod a példányt, hogy minden műveletet, amelyhez kell az unmanaged resource, ObjectDisposedException-t dobjon). Magáért a példány felszabadításáért továbbra is a GC felelős.

Sem a System.Windows.Window, se a DispatcherTimer nem IDisposable, tehát a fent írt három (na jó, kettőért vagy te magad felelős) lépésen kívül semmi mást nem kell tenned.
Mutasd a teljes hozzászólást!

  • Ha lambdát használ az esmény kezeléshez akkor elvileg automatikusan kéne megszűnnie a dolgoknak, ahogy megszűnik a timer gazdája.
    Mutasd a teljes hozzászólást!
  • Rákerestem a lambdára, de a látottak alapján nem értem, hogy mit tudnék kezdeni vele. Vagy csak simán rosszul kerestem/értelmeztem.
    Jelenleg ennyit írok a konstruktoba:
       dispatcherTimer.Tick += new EventHandler(KépFrissítés);
       dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1, 500);
       dispatcherTimer.Start();

    H.Lilla segítségével, már így néz ki a Closed eseményem:
       dispatcherTimer.Stop();
       dispatcherTimer.Tick -= KépFrissítés;

    Rosszul tudom, hogy a Window példányának a megszüntetésével nem kell foglalkoznom? Azzal nem foglalkozom, csak bezárom ("Close()"). Lehet, hogy már itt elrontok valamit? A "ShowDialog()" után még egy értéket kiolvasok a példányból, és többet semmire sem használom. Feltételeztem, hogy ezt már a GC lekezeli.
    Mutasd a teljes hozzászólást!
  • A lambda kifejezés használata leiratkozás szempontjából veszélyesebb.

    timer.Tick += (sender, args) => Debug.WriteLine("timer triggered"); timer.Tick += (sender, args) => Debug.WriteLine("timer triggered"); //ez a lambda nem ugyanaz, mint az első lambda timer.Tick -= (sender, args) => Debug.WriteLine("timer triggered"); //ez pedig ismét egy újabb lambda, ami nem ugyanaz, mint az első kettő
    A leiratkozás ebben az esetben nem fog megtörténni, mert a harmadik lambda sosem iratkozott fel az eseményre. Azt kell megérteni, hogy egy lambda ugyanúgy viselkedik, mintha egy objektumot példányosítanál. Akárhányszor új lambdát írsz le (még ha ugyanaz is a kódja), az egy példányosításnak felel meg. Még rosszabb a helyzet, ha a lambda hivatkozik más változókra is, mert akkor még Closure is létrejön:

    public MyWindow() { InitializeComponent(); var x = 1; timer.Tick += (o, e) => Debug.WriteLine(x++); //emiatt a compier egy külön osztályt is generál }
    Tovább súlyosbíthatjuk a helyzetet, ha az osztály egy tagját hivatkozzuk:

    class MyWindow : Window { private int x = 1; public MyWindow() { InitializeComponent(); timer.Tick += (o, e) => Debug.WriteLine(x++); } }
    Ekkor a generált Closure-ben az egész "this" (a MyWindow példány) capture-ölve lesz, ami memory leak-hez vezet, hiszen a GC sosem fogja tudni felszabadítani se a DispatcherTimer-t, se a MyWindow példányt, mivel hivatkoznak egymásra.

    Rosszul tudom, hogy a Window példányának a megszüntetésével nem kell foglalkoznom?

    Pontosan, nem kell vele foglalkoznod. Ha valami nem IDisposable (és megbeszéltük, hogy a System.Windows.Window nem az), akkor nem kell külön foglalkozni vele. Tehát valami ilyesmi kódod lehet:

    private void Button_Clicked(object? sender, EventArgs e) { MyDialog dialog = new MyDialog(); if (!dialog.ShowDialog()) return; var input = dialog.SomeUserInputProperty; //... } //a hatáskör vége miatt a dialog változó megszűnik a stack-ről, a heap-en az objektum reference countere 0-ra csökken, a GC felszabadítja majd egyszer
    A MyDialog.Closed pedig tartalmazza az általad írt kódot. Ez így teljesen rendben van, és ahogy írtad, a GC kezeli majd.
    Mutasd a teljes hozzászólást!
  • Lambdabal nem kell leiratkozni, nem is tudnal, ha akarnal se. Ez automatikusan megtortenik, mivel itt peldany nelkuli metodusrol van szo es azon nincs mit felszabaditani, nyugodtan megszuntetheto.
    Mutasd a teljes hozzászólást!
  • Nagyon köszi!

    Így már a lambda is "közelebb került hozzám". Ezt úgy vélem, hogy talán majd a conrtol-oknál tudnám hasznosítani. De ott még nem tartok. Jelen esetben egyszerűbbnek érzem a "sima" leiratkozást.
    Igen, gyakorlatilag "ugyanez" van az én kódomban is.

    A "Debug.WriteLine"-t külön köszi. Most látom először, de úgy érzem, hogy hamarosan nagy haverom lesz...
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd