Zum Inhalt springen

AddRange wird immer langsamer?!


errox

Empfohlene Beiträge

Hallo Freunde,

und wieder ein Problem.

Also ich schilder es euch kurz mal:

Ich hab ein Panel in dem ich Buttons ausrichte. Die immer wieder neu erzeugt werden (temporär). Dabei ist mir was "tolles" aufgefallen

Code:


            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

            sw.Start();


            foreach (Button btn in panel1.Controls)

            {

                btn.Dispose();

            }


            panel1.Controls.Clear();


            List<Button> list = new List<Button>();


            int size = 50;


            for (int i = 0; i < 60; i++)

            {

                for (int ii = 0; ii < 7; ii++)

                {

                    Button btn = new Button();

                    btn.Location = new Point(ii * size, i * size);

                    btn.Size = new Size(size, size);

                    btn.Text = "A";


                    list.Add(btn);

                }

            }


            panel1.Controls.AddRange(list.ToArray());


            sw.Stop();


            lblDuration.Text +=  sw.ElapsedMilliseconds + "\r\n";

Man braucht immer länger, bis sich die Liste Initialisiert hat. ABschnitt von lblDuration:

197

590

...

652

...

768

794

...

877

...

...

1025

Das beste, nach einer bestimmten Zeit krieg ich bei AddRange eine Win32 Exception (Fehler beim Erstellen des Fensterhandles.)

Warum dauert es immer länger? ich bereinige doch die Ressoucen mit Dispose und Clear es vorher sogar.

Wisst ihr ne lösung? Gruß und Danke!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Nur weil man im .NET ein Dispose absetzt, heißt das noch lange nicht, das der GC das Element auch zur selben Zeit aus dem Speicher entfernt.

Du könntest einfach mal ein GC.Collect() absetzten und den GC somit zwingen nicht verwendeten Speicher freizugeben. Oder alternativ GC.WaitForPendingFinalizers().

lg

Gateway

Link zu diesem Kommentar
Auf anderen Seiten teilen

GRade zu Demozwecken 420 (7 * 60) mal.
Ich meinte, wie oft der ganze oben gezeigte Code ausgeführt wird. Dass du da 7 * 60 Buttons erstellst, sehe ich selbst. Aber wie oft passiert das?

Ins Verhalten des GC solltest du nur dann eingreifen, wenn du ganz genau weißt, was du tust, und wenn du sicher bist, dass du das Problem nicht anders lösen kannst.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Nur weil man im .NET ein Dispose absetzt, heißt das noch lange nicht, das der GC das Element auch zur selben Zeit aus dem Speicher entfernt.

Du könntest einfach mal ein GC.Collect() absetzten und den GC somit zwingen nicht verwendeten Speicher freizugeben. Oder alternativ GC.WaitForPendingFinalizers().

lg

Gateway

Ich werd da mal was Probieren, Danke!

Ich meinte, wie oft der ganze oben gezeigte Code ausgeführt wird. Dass du da 7 * 60 Buttons erstellst, sehe ich selbst. Aber wie oft passiert das?

Ins Verhalten des GC solltest du nur dann eingreifen, wenn du ganz genau weißt, was du tust, und wenn du sicher bist, dass du das Problem nicht anders lösen kannst.

1 Mal drück ich auf den Button. Das mit dem Clear / AddRange läuft aber vllt max. 30 mal durch.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich werd da mal was Probieren, Danke!
Bitte nicht versuchen, solche Probleme durch Rumfummeln am GC zu lösen. In den allerallermeisten Fällen sollte man von GC.Collect die Finger lassen.

1 Mal drück ich auf den Button. Das mit dem Clear / AddRange läuft aber vllt max. 30 mal durch.
Ist da noch eine Schleife drumherum, oder warum löst ein einziger Klick aus, dass das 30 Mal ausgeführt wird?
Link zu diesem Kommentar
Auf anderen Seiten teilen

und wofür?

"Kassensystem"

Bitte nicht versuchen, solche Probleme durch Rumfummeln am GC zu lösen. In den allerallermeisten Fällen sollte man von GC.Collect die Finger lassen.

Wie dann? :confused: Ich mach doch alles richtig, oder nicht?

Ist da noch eine Schleife drumherum, oder warum löst ein einziger Klick aus, dass das 30 Mal ausgeführt wird?

Nein, nur die Methodendeklaration.

Das ist der grund, dass es beim "Neuinitialisieren" die Buttons vom Panel alle entfernt werden, und wieder neu eingefügt. buttons können neu hinzugefügt werden, das Panel kann gecleared werden und die Buttons ausgerichet, etc.

Das ist nur die "basis" um zu testen wo das Problem liegt. Besser als würd ich das von hand machen :D

Link zu diesem Kommentar
Auf anderen Seiten teilen

Bitte nicht versuchen, solche Probleme durch Rumfummeln am GC zu lösen. In den allerallermeisten Fällen sollte man von GC.Collect die Finger lassen.

Was heißt hier Rumfummeln :P. Die Funktionen sind in keinster weise schädlich. Dem GC wird nur ein kleiner schubser gegeben. Frei nach dem Motto: "Beweg dein hintern es gibt was zu tun".

Wir nutzten hier die Funktion eigentlich regelmäßig (müssen es sogar), da der GC scheinbar etwas langsam zu potte kommt. Wenn die Funktion nicht wäre hätte ich des öffteren OutOfMemory Exceptions und das obwohl die Kiste laut TaskManager noch 1.5 GB freien Ram hat. Und ja wir Zerstören eigentlich die großen Datenkonstrukte, wenn Sie nicht mehr gebraucht werden. Das Problem ist nur das es ewig dauert bis Sie dann auch wirklich freigegen sind.

Deswegen ==> GC.Collect()

Also ich würd die Funktion nicht so verteufeln.

lg

Gateway

Bearbeitet von Gateway_man
Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich mach doch alles richtig, oder nicht?
Keine Ahnung, dafür hast du nicht genug Informationen geliefert. Es ist immer noch nicht klar, in welchem Kontext dein Code steht.

Nein, nur die Methodendeklaration.
Ich verstehe nur Bahnhof. Erklär mir bitte, wie ein einzelner Klick dazu führen kann, dass dieser Code 30 Mal ausgeführt wird.

Das ist nur die "basis" um zu testen wo das Problem liegt.
Was genau ist denn das eigentliche Problem, das dich dazu gebracht hat, diesen Testcode zu erstellen?
Link zu diesem Kommentar
Auf anderen Seiten teilen

Was heißt hier Rumfummeln :P. Die Funktionen sind in keinster weise schädlich.
Ist dir das Generationenkonzept des GC bekannt? Jeder Aufruf von Collect bewirkt, dass nicht freigegebene Objekte in eine höhere Generation kommen. Ein Aufruf von Collect zum falschen Zeitpunkt kann sich nachteilig auf Performance und Speicherverbrauch auswirken.

Außerdem pfuscht man damit im Selbst-Tuning des GC herum.

Wenn die Funktion nicht wäre hätte ich des öffteren OutOfMemory Exceptions und das obwohl die Kiste laut TaskManager noch 1.5 GB freien Ram hat. Und ja wird Zerstören eigentlich die großen Datenkonstrukte, wenn Sie nicht mehr gebraucht werden.
Es gibt Fälle, in denen die Verwendung von GC.Collect angebracht ist. Aber keinesfalls sollte das eine allgemeine Empfehlung zum Lösen von Speicherproblemen sein.

Also ich würd die Funktion nicht so verteufeln.
Ich schon. Nur wenn man genau weiß, wie der GC arbeitet, sollte man ihm ins Handwerk pfuschen.
Link zu diesem Kommentar
Auf anderen Seiten teilen

Okey. Ich versuch mal die funktion zu erklären. Vllt wirds einem klar. Wenn fragen sind, melden.

Ich hab ein programm. Soweit so gut :D

In dem programm ist ein panel. In dem programm kann man eine anzahl eingeben, wieviele buttons in das panel hinzugefügt werden sollen. Diese buttons können durch eine klasse verschoben werden (das ist eine andere geschichte).

Ausserdem kann ich durch ein button das panel leeren.

Funktion: kassensystem und patchworkart für meine nichten.

Soweit sogut.

Dabei ist mir aufgefallen, wenn ich jedesmal 500 buttons neu hinzufüg und lösch, gibts probleme. Aber erst wenn ich das einigemale mach.

Alternative: neu ausrichten.

Nicht möglich, da ich vllt von 500 auf 10 buttons will, und dann wiedet auf 400, etc.

Also der lösungsansatz controls neu hinzufügen / entfernen bleibt. Weil ich möchte jetzt auch selber wissen wie ich das beheben kann.

Deswegen hab ich eine form

Zum testen gemacht, die nix anderes tut als buttons hinzufügen und entfernen, um der sache auf den grund zu gehen. Ohne click events, erc.

Jedoch komme ich hier nicht weiter, da ich nicht weiss wie ich was und wie aufrufen soll, damit ich keine win32 exception bekomm beim addrange und das nicht jedesmal länger dauert.

Ich hoff alle fragen sind geklärt :)

Danke für eure hilfe!!!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dabei ist mir aufgefallen, wenn ich jedesmal 500 buttons neu hinzufüg und lösch, gibts probleme.
An dieser Stelle wären jetzt mehr Details toll gewesen. Mit "es gibt Probleme" kann man so gar nichts anfangen.

Ein GUI mit 500 Buttons ist in meinen Augen unbedienbar. Tritt das Problem auch bei "normaler" Verwendung auf?

Deswegen hab ich eine form

Zum testen gemacht, die nix anderes tut als buttons hinzufügen und entfernen, um der sache auf den grund zu gehen. Ohne click events, erc.

Jedoch komme ich hier nicht weiter, da ich nicht weiss wie ich was und wie aufrufen soll, damit ich keine win32 exception bekomm beim addrange und das nicht jedesmal länger dauert.

Grob gesagt ist jeder Prozess auf 10.000 Fenster beschränkt. Wenn der GC in deinem Fall nicht dazu kommt, die alten abzuräumen, wird dein Prozess irgendwann keine neuen Fenster mehr erstellen können. Das dürfte der Grund für die Exception sein.

Es kann sein, dass du das durch Erzwingen der Garbage Collection beheben kannst. Solange es nur dazu dient, in einem Testprogramm ein anderes Problem reproduzierbar zu machen, kann man das vertreten.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Nur weil man im .NET ein Dispose absetzt, heißt das noch lange nicht, das der GC das Element auch zur selben Zeit aus dem Speicher entfernt.

Du könntest einfach mal ein GC.Collect() absetzten und den GC somit zwingen nicht verwendeten Speicher freizugeben. Oder alternativ GC.WaitForPendingFinalizers()

GC.Collect() heißt nicht, dass ein Collect initiiert wird; sondern metaphorisch gesehen schwenkst Du ein rotes Fähnchen mit dem Du signalisierst, dass der GC sich eventuell ein wenig beeilen sollte, einen Collect durchzuführen.

Die Funktionen sind in keinster weise schädlich. Dem GC wird nur ein kleiner schubser gegeben. Frei nach dem Motto: "Beweg dein hintern es gibt was zu tun".

Wie schon dargelegt worden ist, ist es in den meisten Fällen eher kontraproduktiv.

Wir nutzten hier die Funktion eigentlich regelmäßig (müssen es sogar), da der GC scheinbar etwas langsam zu potte kommt. Wenn die Funktion nicht wäre hätte ich des öffteren OutOfMemory Exceptions und das obwohl die Kiste laut TaskManager noch 1.5 GB freien Ram hat. Und ja wir Zerstören eigentlich die großen Datenkonstrukte, wenn Sie nicht mehr gebraucht werden. Das Problem ist nur das es ewig dauert bis Sie dann auch wirklich freigegen sind.

Deswegen ==> GC.Collect()

Ich würde mir an der Stelle Gedanken um die Programmierung machen.

Nochmal: C# ist kein C++ und managed heißt eben managed. Wer das nicht will, sollte über die Wahl seines Werkzeuges nachdenken.

Einzig sinnvolles Einsatz-Szenario von Dispose() ist in eigenen Objekten, welche selbst mit unmanaged code angereichert sind, dass in dem Fall Speicher freigegeben wird.

Zum Thema GC.Collect()

Rule #1

Don't.

This is really the most important rule. It's fair to say that most usages of GC.Collect() are a bad idea and I went into that in some detail in the orginal posting so I won't repeat all that here.

@TO

Ich bezweifele gerade, dass Du Dir Gedanken um eine Lösung gemacht hast.

Auch ohne im Detail zu wissen, resp. verstanden zuhaben, was Du vorhast, hört sich Deine Lösung recht unausgegoren an.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Moin!

Ich glaube Deine Verwunderung bei der foreach-Schleife liegt daran, dass Du eine wichtige Regel der foreach-Schleife nicht verinnerlicht hast:

Sie sollte nicht eingesetzt werden, um die Inhalte der Auflistung zu ändern, sodass unvorhersehbare Nebeneffekte vermieden werden.

Nachzulesen hier:

Über foreach bei MSDN

Und Du hast recht, dass ist für alle interessant und diese Regel sollte einem in Mark und Bein übergehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

in deinem Kassensystem fügst du dynamisch sehr viele Buttons hinzu

wofür?

was machen die?

warum müssen es viele sein?

ich denke, das ist dein Problem: du brauchst diese vielen Buttons eigentlich gar nicht, verwendest sie aber dennoch

was ist überhaupt mit "Kassensystem" gemeint?

was macht dein Programm?

was macht der Benutzer mit deinem Programm?

es fehlen immernoch Details, ohne die dir keine richtig helfen kann

viele versuchen es zwar dennoch, aber da Details fehlen, kommt es zu keinem Ergebnis

Link zu diesem Kommentar
Auf anderen Seiten teilen

@SilentDemise

Dispose aufruf via foreach fällt aber nicht unter die Regel, Methodenaufrufe sind völlig i.O.

Hast Du naehere Informationen dazu bzw einer Erklaerung dafuer, warum der Dispose()-Aufruf ausgenommen ist? Ich haette jetzt ehrlich gesagt vermutet, dass durch das Dispose() die Auflistung geaendert wird. Konnte bei einer google-Suche nichts dazu finden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...