Speicher freigeben, Threads, ProgressDialog...

Alles rund um die Programmierung mit Qt
acdc
Beiträge: 82
Registriert: 23. Oktober 2007 18:56

Speicher freigeben, Threads, ProgressDialog...

Beitrag von acdc »

Hallo,
ich habe nun wieder einmal eine QT-Projekt gestartet. Nun habe ich ein Hauptfenster (QMainWindow) mit einem TabWidget und in diesem Tabwidget befindet sich probehalber ein button (Start-Button).
Mit diesem Button starte ich nun zwei Threads. Die Beiden Threads verarbeiten jeweils einen Teil einer Datei und der Vortschritt wird in einem ProgressDialog angezeigt. Soweit, sogut!

Mit dem ProgressDialog kann man die beiden Threads abschalten [mit terminate() ] durch einen Klick auf "cancel"

Nun meine Frage:
Wie kann ich den Speicher, den ich belegt habe, wenn ich auf den Start-Button gedrückt habe und danach auf cancel bei dem ProgressDialog klicke wieder freigeben.
Wiederhole ich nämlich oftmals dieses Starten und Abbrechen, so wird der Speicherverbrauch laut Taskmanager immer größer.
mit new erstelle ich jeweils nur die threads, den ProgressDialog und zwei QFile.
Mein Programm benötigt nach dem Start 5MB, nach dem ersten Versuch 10Mb und danach 17 usw.
Habe ich hier irgendwo einen Denkfehler bei dem Freigeben mit 'delete'..?

Habe noch einen Teil des codes angehängt, ich hoffe es gibt eine einfache Lösung und freu mich auf eure Antworten!

acdc
Dateianhänge
converterwidget.cpp
(3.93 KiB) 164-mal heruntergeladen
converterwidget.h
(804 Bytes) 175-mal heruntergeladen
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Auf Anhieb kann ich nichts sehen. Ggf. ist in den Konverter-Threads noch ein memleak?
QFile würde ich nicht mit new anlegen. Den ProgressDialog auch nicht. Da sind Klassenmember schöner weil man dann delete nicht vergessen kann. Ist ber Geschmckssache.

/edit: Wenn das ganze unter Linux kompiliert -> wie immer mein Hinweis auf 'valgrind --leak-check=full'
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
fthieme
Beiträge: 10
Registriert: 19. Mai 2007 22:47

Beitrag von fthieme »

Kurz als Vorrede: ein Nullpointer gehört auf NULL gesetzt und mit NULL verglichen, nicht mit 0.

Nach kurzem Überfliegen deines Codes ist mir nichts aufgefallen. Machst du noch dynamisches innerhalb von ConverterThread? Nicht dass dort beim terminate() nicht alles freigegeben wird...
acdc
Beiträge: 82
Registriert: 23. Oktober 2007 18:56

aw:

Beitrag von acdc »

Christian81 hat geschrieben:Auf Anhieb kann ich nichts sehen. Ggf. ist in den Konverter-Threads noch ein memleak?
QFile würde ich nicht mit new anlegen. Den ProgressDialog auch nicht. Da sind Klassenmember schöner weil man dann delete nicht vergessen kann. Ist ber Geschmckssache.

/edit: Wenn das ganze unter Linux kompiliert -> wie immer mein Hinweis auf 'valgrind --leak-check=full'

----
Danke für die Antwort!

In den Threads reserviere ich keinen Speicher mit new...
--> habe die datei angehängt!

Ich benutze NetBeans 6.8 und QT version 4.6.1 - solche Probleme haben mich schon oft von der Fertigstellung meiner Programme gehindert und ich weis einfach nicht wie ich das Problem mit dem Speicher beheben soll.

Werde das Ganze morgen mit dem QCreator versuchen und sehen ob es das Gleiche ist - sonst hab ich derzeit keine Idee!

acdc
Dateianhänge
converterthread.cpp
(3.36 KiB) 173-mal heruntergeladen
Zuletzt geändert von acdc am 7. Februar 2010 22:40, insgesamt 1-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

fthieme hat geschrieben:Kurz als Vorrede: ein Nullpointer gehört auf NULL gesetzt und mit NULL verglichen, nicht mit 0.
Als Beispiel stddef.h von gcc 3.4.5

Code: Alles auswählen

#ifdef __GNUG__
# define NULL __null
#else   /* G++ */
# ifndef __cplusplus
#  define NULL ((void *)0)
# else   /* C++ */
#  define NULL 0
# endif  /* C++ */
#endif  /* G++ */
Soviel zu NULL :D
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
fthieme
Beiträge: 10
Registriert: 19. Mai 2007 22:47

Beitrag von fthieme »

Christian81 hat geschrieben: Als Beispiel stddef.h von gcc 3.4.5

Soviel zu NULL :D
Und wenn du keinen GCC nimmst? Und wenn du nicht auf x86 bist? Und wenn sich die Implementierung von NULL mal ändert? Und wenns nur allein aus dem Grund ist, dass es die Lesbarkeit deutlich erhöht? ;)

Edith: Nur mal so als Vergleich SunStudio 12u1 auf OpenSolaris, unistd.h

Code: Alles auswählen

#ifndef NULL
#if defined(_LP64)
#define NULL    0L
#else
#define NULL    0
#endif
#endif
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Lesbarkeit las ich durchgehen. Aber wir schweifen vom Thema ab...

/edit: Wenn wir schon dabei sind - alle checks der Pointer auf != 0 sind überflüssig :D
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
fthieme
Beiträge: 10
Registriert: 19. Mai 2007 22:47

Re: aw:

Beitrag von fthieme »

acdc hat geschrieben: Ich benutze NetBeans 6.8 und QT version 4.6.1 - solche Probleme haben mich schon oft von der Fertigstellung meiner Programme gehindert und ich weis einfach nicht wie ich das Problem mit dem Speicher beheben soll.

Werde das Ganze morgen mit dem QCreator versuchen und sehen ob es das Gleiche ist - sonst hab ich derzeit keine Idee!
Warum soll die Entwicklungsumgebung was mit dem Speicherverbrauch deines Programms zu tun haben?

Also ich würde auf jeden Fall das probieren:
Christian81 hat geschrieben: /edit: Wenn das ganze unter Linux kompiliert -> wie immer mein Hinweis auf 'valgrind --leak-check=full'
acdc
Beiträge: 82
Registriert: 23. Oktober 2007 18:56

Re: aw:

Beitrag von acdc »

fthieme hat geschrieben:
acdc hat geschrieben: Ich benutze NetBeans 6.8 und QT version 4.6.1 - solche Probleme haben mich schon oft von der Fertigstellung meiner Programme gehindert und ich weis einfach nicht wie ich das Problem mit dem Speicher beheben soll.

Werde das Ganze morgen mit dem QCreator versuchen und sehen ob es das Gleiche ist - sonst hab ich derzeit keine Idee!
Warum soll die Entwicklungsumgebung was mit dem Speicherverbrauch deines Programms zu tun haben?

Also ich würde auf jeden Fall das probieren:
Christian81 hat geschrieben: /edit: Wenn das ganze unter Linux kompiliert -> wie immer mein Hinweis auf 'valgrind --leak-check=full'
valgrind ist für linux, eine Windows Version habe ich nicht gefunden. Kennt jemand einen freien Ersatz?

Eigentlich sollte die entwicklungsumgebung nichts damit zutun haben, aber man weis ja nie - ich hatte da schon einige Überraschungen.

Danke für eure Tips - vielleicht folgen noch ein paar!
mfg acdc
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Mit dem ProgressDialog kann man die beiden Threads abschalten [mit terminate() ] durch einen Klick auf "cancel"
Das ist ein "gewaltsames" Beenden durch das OS... bist du sicher, dass du da durch die (Qt-)Destruktoren kommst?
acdc
Beiträge: 82
Registriert: 23. Oktober 2007 18:56

Beitrag von acdc »

solarix hat geschrieben:
Mit dem ProgressDialog kann man die beiden Threads abschalten [mit terminate() ] durch einen Klick auf "cancel"
Das ist ein "gewaltsames" Beenden durch das OS... bist du sicher, dass du da durch die (Qt-)Destruktoren kommst?
super Idee, darauf hätte ich auch selber kommen können!
Danke, das Problem hat sich soweit erledigt - hoffe es bleibt so, DANKE!!!

Jetzt noch eine Frage, wie kann ich den QProgressDialog richtig beenden, ohne dass etwas im Speicher bleibt?

mfg acdc
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Sauber wird der Thread beendet, indem irgend eine Art "bitte-beende-dich"-Event (Signal, Flag oder Mutex & CO) an den Thread gesendet wird und dieser dann darauf reagiert. In deinem Fall ist es auch noch wichtig, dass die GUI auf die Beendigung wartet, weil sonst die "cancel()"-Methode die Ressourcen weglöscht, während der Thread noch läuft.

Es gibt viele Varianten.. weil du in der GUI nach auf das Ende warten musst, wäre ein Mutex eine Lösung. Du könntest ja den gleichen Mutex brauchen, um das Beenden zu bewirken und auch gleich darauf zu warten...

Code: Alles auswählen

void ConverterThread::abortConverter() // public slot
{
 // evt. auch tryLock mit Timeout...
  mMutex.lock;  //  thread beenden und warten, bis thread fertig ist
}

void ConverterThread::run()
{
  ...
  else
  {
      while (!fin1.atEnd())
      {
        ...
        if(zeilenZahl<intervallStart)
          continue;

        if(zeilenZahl>=intervallEnd)
        {
          fin1.close();
          break;
        }

        if (!mMutex.tryLock()) // GUI moechte Thread beenden...
          break;
        .....
        mMutex.unlock();
  }
  mMutex.tryLock(); // falls niemand darauf wartet..
  mMutex.unlock();  // GUI darf weiter..
}
in der GUI dann nicht mehr zu "terminate()", sondern zu "abortConverter()" connecten.. und evt. die Threads mit "thr1->deleteLater()" löschen, nicht mit "delete thr1"..

Denk dir das aber selbstständig nochmals durch (ist nur so zwischen Tisch und Stuhl hingeschrieben..), aber das Prinzip sollte so etwas klarer werden.

[EDIT] Code formatiert
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Wird der Slot nicht eventuell (je nach Konzept) im Thread-Kontext ausgeführt?

[edit] Noch aus Interesse: Was genau wird nicht aufgeräumt, wenn man den Thread "gewaltsam" per ::terminate beendet?
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

pfid hat geschrieben:Wird der Slot nicht eventuell (je nach Konzept) im Thread-Kontext ausgeführt?
Prinzipiell: ja.. in diesem Fall: nein (keine "QueuedConnection", kein "moveToThread" und keine Eventloop in der run-Methode.
pfid hat geschrieben: [edit] Noch aus Interesse: Was genau wird nicht aufgeräumt, wenn man den Thread "gewaltsam" per ::terminate beendet?
IMHO wird das OS einfach den Thread (im Linux-Kernel einfach ein "struct") rausschmeissen... so rein gefühlsmässig würde ich also sagen: nichts wird aufgeräumt, ausser dem Stack. Mit etwas Glück evt. auch noch Filedescriptoren.
Ein kurzer Blick in den Source zeigt, dass z.B. unter Windows "TerminateThread" aufgerufen wird.. und MS schreibt dazu "is a dangerous function that should only be used in the most extreme cases" und "the heap lock will not be released." und "the target thread has no chance to execute any user-mode code" (zu letzterem gehören auch Destruktoren).

Daher: finger weg..
:wink:
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Finger weg ist in meinen Augen etwas übertrieben. Was wenn der Thread aus irgendwelchen Gründen hängt, und sich nicht beenden lässt? Dann würde die ganze Applikation hängen (im Beispiel irgendwo in einem der Mutex-Locks vermutlich). Natürlich sollte die terminate Funktion nicht zum regulären Beenden verwendet werden.
Allerdings halte ich es für fragwürdig, dass durch das bloße canceln von Threads irgendwelche Speicherleks entstehen. Die OS spezifischen Ressourcen des Threads muss das OS wegräumen, auch wenn man den Thread gewaltsam beendet. Alles was der User allokiert hat, muss logischerweise der User wegräumen, was allerdings von der Nebenläufigkeit unabhängig ist.
Sprich: das Objekt kann der User auch nach QThread::terminate löschen, wenn dort immer noch der Speicher wächst, fehlt vielleicht irgendwo ein delete?

Aufpassen sollte man allerdings mit z.B. Mutexobjekten, die während dem Terminieren gelockt sind. Ich weiß nicht wie es unter Windows ist, in der POSIX Implementierung werden diese Mutexobjekte jedenfalls zu Zombies, bei denen jede weiteren Lockversuche im Deadlock enden. Allerdings gibts dafür entsprechende Methoden (Cleanup-Handler), um auch nach pthread_cancel noch sauber aufzuräumen. Gibt es dazu was von Qt, oder darf bei Qt einfach niemals ein Thread irgendwo stehenbleiben?
Antworten