Application->ProcessMessages(), SendMessage() oder Thread?
-
Dir scheinen aber noch ein paar grundlegende C++ Kenntnisse zu fehlen.
Du solltest doch wissen, wie man eine Member-Variable in einer Klasse deklariert und darauf zugreift? (auch TEineFreqThread ist eine ganz normale Klasse)
-
Hallo Th69,
das war nicht ganz die Antwort die ich mir erhofft habe. Aber ich gebe dir Recht, dass ich mich noch nicht seit Jahren mit C++ beschäftige. Ich hatte drei Jahre C in der Schule (ein mal pro Woche C Unterricht für 2 Schulstunden).
Das was ich über C++ weiß, lerne ich aus Büchern und vor allem aus diesem Forum, weil ich einige Funktionen in meinem Projekt benötige, die mit C nur sehr umständlich oder gar nicht zu realisieren sind.
Wenn du mir nun sagst, dass ich keine Membervariablen angelegt habe, dann meinst du sicher die normalen Variablen im Private Bereich.
Ich habe dort diese Variablen angelegt:
//-- Variablen ------------------------------------------------------ unsigned long int messfreq; // Messfrequenz int pegel; // Pegel int anzahl; // Anzahl bool dauermessung; // Dauermessung //-------------------------------------------------------------------
Ich habe diese auch mit den Werten von TFEineFreq beschrieben und habe einen Zugriff eben mit meinen Initialisierten Werten 0.
Wenn ich versuche in meiner TEineFreqThread Klasse die Klasse für TFEineFreq anzulegen. (Header Datei)
TFEineFreq *EineFreq;
Und dabei spielt es keine Rolle ob ich sie im Private oder Testhalber im Public Bereich reinschreibe die Fehlermeldung:
Typname erwartet, in Deklaration fehlt ; ungültige Verwendung von typedef 'exception'Gebe ich nun
class TFEineFreq Test;
im Public Bereich ein bekomme ich die Fehlermeldung Undefinierte Struktur TFEineFreq.
Meine Headerdatei für diese Klasse TFEineFreq ist eingebunden und da ich
das so auch in anderen Klassen mache, verstehe ich das nicht.Also muß ich entweder Thread anders behandeln oder ich habe da einen Fehler
Oder ich muß einen anderen Konstuktor für meine TEineFreqThread Klasse anlegen, aber sobald ich das mache:
TEineFreqThread(TFEineFreq &a);
bekomme ich die Fehlermeldung, das die Deklaration nicht abgeschlossen wäre.
Wo kann ich denn nun meine grundlegenden Kenntnisse vertiefen und mein Problem nachlesen?
-
Ok, dann mal extra für dich:
// in EineFreqThread.h
class TFEineFreq; // forward declaration class TEineFreqThread { public: __fastcall TEineFreqThread(bool CreateSuspended, TFEineFreq *frame); virtual void __fastcall Execute(); private: TFEineFreq *m_frame; // member };
// in EineFreqThread.cpp
#include "EineFreqThread.h" #include "FEineFreq.h" __fastcall TEineFreqThread::TEineFreqThread(bool CreateSuspended, TFEineFreq * frame) : TThread(CreateSuspended), m_frame(frame) // member initialization { FreeOnTerminate = true; } void __fastcall TEineFreqThread::Execute() { // ... // use of frame m_frame->SomeMethod(); // call of your frame method // ... }
Und nun noch das Erzeugen des Threads:
TEineFreqThread *my_thread = new TEineFreqThread(false, FStandHeizung); // ...
Wenn du wiederum Zugriff auf den Thread innerhalb deiner Form-Klasse benötigst, dann mußt du auch "my_thread" als Member-Variable in deiner Form-Klasse anlegen.
Du solltest dir noch mal den Unterschied zwischen lokalen Variablen und Klassen-Variablen (Membern) klarmachen.
So ich hoffe, das hilft dir...
-
Hallo Th,
erstmal vielen herzlichen Dank für deine Hilfe. Ich habe ein wenig gebraucht um diese Vorgehensweise richtig zu verstehen. Habe nun also dem jeweiligem Thread meine Frames übergeben. Diese Codezeilen habe ich entfernt:
// Frame auf Aktuellen Frame setzen AktuellFrame = FStandheizung;
Da ich an dieser Stelle ja den Frame dem Thread übergebe und nicht mehr in meinem Hauptformular habe.
Ich kann jetzt alle Aufgaben im Frame erledigen, dadurch brauche ich in der Execute Funktion so gut wie keine Anweisungen.
Ich habe nun in meinem Hauptformular noch einige Codezeilen eingefügt:
// Auf EineFreq Thread casten TThreadEineFreq *EineFreq = reinterpret_cast<TThreadEineFreq*> (AktuellThread); // Prüfen ob ein MessungsThread bereits läuft if((AktuellThread) && (EineFreq->Status() != true)) { // Meldung ausgeben ShowMessage("Eine Messung läuft bereits"); } else { // Wenn ein Thread besteht if(AktuellThread) { // Thread löschen DeleteThread(); }
In der DeleteThread Funktion führe ich folgenden Code aus:
void TFormGrundbild::DeleteThread(void) { // Thread terminieren AktuellThread->Terminate(); // AktuellThread auf NULL setzen AktuellThread = NULL; }
Was ich da aber nun nicht ganz verstehe ist: Wenn ich nur AktuellThread->Terminate(); ausführe, dann habe ich nicht das Gefühl, dass mein Thread auch beendet wird. Zumindest muß ich ihn explizit auf Null setzen, damit ich ihn wieder benutzen kann und wird mein Frame jetzt eigentlich noch gelöscht oder wird er mit dem Thread einfach deleted?
Ich rufe ja kein Delete von dem Frame mehr auf. Kann ich das im Borland Compiler irgendwo nachschauen, ob der noch reservierten Speicher für das Frame hat oder ob der Freigegben wird.
Normalerweise hatte ich sonst in CodeGuard immer die Meldung, dass noch Speicher verwendet wird, aber die kommt nicht.
Vielleicht ist das auch schon richtig so.
Grüße und nochmals vielen Dank.
egcactus
-
Hi, schön, daß du es verstanden hast -)
Bzgl. der Freigabe des Frames:
es kommt darauf an, wen du als Owner (d.h. als Konstruktor-Parameter) beim Frame angegeben hast. Wenn du Application oder ein Formular angegeben hast, dann wird der Frame automatisch beim Schließen der entsprechenden Komponente freigegeben. Nur wenn du 0 (NULL) angegeben hast, bist du selber für die Freigabe verantwortlich.Wenn der Frame nur von dem Thread benutzt wird, dann könntest du ihn beim Beenden des Threads mitfreigeben (dementsprechend 0 als Frame-Parameter eintragen).
Und bzgl. TThread-Terminate():
diese Methode sorgt nur dafür, daß die Variable 'Terminated' gesetzt wird und meistens hat man im Thread die folgende Schleife:void __fastcall TMyThread::Execute() { while (!Terminated) { // ... } }
Aber schau dir mal die Erklärung in der BCB-Hilfe zu den einzelnen TThread Eigenschaften und Methoden an.
-
Hallo Th, ich habe die while Funktion bereits drin gehabt, nur eben so wie unten programmiert. Nun habe ich etwas erschreckendes feststellen müssen.
Sobald ich mein Frame in den Thread übergebe geht meine CPU Auslastung auf 100% hoch. Und kommt nur wieder runter, wenn ich das Projekt beende.
Wie es aussieht liegt es gerade an dieser Schleife. Mein Thread fragt, darin ab ob im Frame der Stop Button betätigt wurde und gibt dies dann an mein Hauptformular zurück.
void __fastcall TThreadEineFreq::Execute() { // Ausführen solange nicht terminiert while(!Terminated) { // Frame Abbruch Variable in Thread übernehmen Abbruch = m_frame->getStatus(); //<<-- Diese Abfrage treibt die CPU auf 100% Auslastung } } bool TThreadEineFreq::Status(void) { // Status zurückgeben return Abbruch; }
Das treibt die CPU Auslastung an die Grenze. Wie ich aber sonst aus meiner Membervariable (Frame) einen kontakt zu meinem Hauptformular herstellen soll, ist mir wohl doch nicht ganz klar.
Habe probiert die priority auf tpIdle zu setzen um nur im Leerlauf die Funktion abzuarbeiten, hat aber auch keine Auswirkung. Muß ich dem da noch irgedwo eine Art PauseMillisekunde spendieren. Kenne sowas aus LabView, da haben die Schleifen so eine Millisekunde als Warteanweisung gebraucht, damit die Funktion nicht mit 100% CPU Auslastung abgearbeitet wurde.
Hast du da eine Idee?
-
Ja, du mußt dem Thread auch mal eine Pause gönnen:
Sleep(0); // gestattet Umschaltung zu anderen Threads, aber ansonsten läuft der Thread weiterhin im Turbo-Modus // oder Sleep(1); // oder beliebiger anderer Wert (in Millisekunden!!!)
Wenn du wirklich nur diese Abfrage drin hast, dann reicht auch ein Sleep(100) oder noch höherer Wert (je nachdem wie schnell der Thread reagieren soll).
-
Super, da bin ich aber beruhigt. Dachte ich müßte da eine größere Änderung vornehmen.
Ich habe wirklich nur diese Abfrage im Thread drin, da alle weiteren Funktionen in meinem Frame abgearbeitet werden.
Zur Erstellung des Frames mache ich das so:
TFEineFreq* FStandheizung = new TFEineFreq(new TGroupBox(FormGrundbild));
Und eigentlich habe ich den dann im Grundbild deletet. Muß ich wahrscheinlich noch mal abändern da ja mein Hauptformular erst zum Programmende beendet wird. Was mich aber gewundert hat, war das ich im Code Guard keine Fehlermeldung bekommen habe, Speicher Leaks. Kann ich unter Borland eigenlich nachschauen, welche Threads von diesem Programm noch laufen?
-
Soviele Fragen auf einmal -)
Aber zuersteinmal: jetzt frage ich mich aber, warum du überhaupt einen Thread verwendest und was in der Funktion frame->getStatus() passiert (denn diese kann ja auch nur passiv einen Wert abfragen)???
Dann noch zu deinem Konstruktor:
warum erzeugst du eine GroupBox (die ja noch nicht einmal einen Parent zugewiesen bekommen hat) und verwendest nicht direkt das Formular als Owner?Und die laufenden Threads kannst du im Builder unter "Ansicht/Debug-Fenster/Threads (Strg+Alt+T))" anzeigen (sofern du einen Haltepunkt gesetzt hast).
Und zu den Speicherleaks kann ich dir nichts Genaueres sagen, da ich dein ganzes Programmkonstrukt nicht so ganz durchschaue (ich habe nur den Eindruck, du machst es komplizierter als es eigentlich ist -).
-
Als ich mit dem Projekt begonnen habe, war es noch nicht so kompliziert aufgebaut. Ich habe einfach über mein TabSheet in dem ich verschiedene Buttons habe meine unterschiedlichen Frames aufgerufen.
Das hat alles sehr gut Funktioniert, bis ich nicht mehr mit Beispielwerten und der CPU Geschwindigkeit getestet habe, sondern reale Hardware dran hatte.
Da hat sich gezeigt, dass wir für einen Messwert ca 1 Sekunde brauchen um den aufzunehmen. Da allein die Komponente FM (Radio) mit einem Frequenzband von 88 MHz bis 108 MHz bei einem Messschritt von 0,1 MHz aufgezeichnet werden soll. Dazu kommt noch, dass jeder Messschritt ca 10 Mal gemessen wird und das einmal mit und einmal ohne eingeschaltetem Frequenzgenerator und da waren wir bei 4000 Sekunden, also 1 Stunde und 11 Minuten für eine Komponente. Und wir haben mehrere.
Da hat sich dann gezeigt, dass der Stop Button natürlich nicht funktionierte. Also habe ich Application->ProcessMessages(); eingefügt.
War dann soweit super, ließ sich stoppen, nur weil ich aber meinen aktuellen Frame ja wie bereits in diesem Thread beschrieben einer übergeordetem Framevariable übergeben habe um in jedem Frame den letzten aktuellen zu Killen hatte ich den neuen Fehler, dass ich Speicherverletzungen bekam. Deshalb der Thread.
Wobei sich da nun noch ein neues Problem gezeigt hat. Meine Drucken Funktion die ich auf den Aktuellen Thread ausgerichtet hatte funktioniert auch nicht mehr, weil mein Frame ja nun in einem Thread läuft. Aber das bekomme ich hin.
Zu frame->getStatus()
In meinem Frame habe ich immer noch die Application->Process() befehle drin, da ja wie bereits erwähnt meine Schleifen sehr lange laufen. Wenn der Button Stop betätigt wird, wird also im Frame eine Variable Abbruch auf true gesetzt. Diese hole ich mir im Thread ab und gebe sie dem Hauptformular.Warum ich eine GroupBox erzeuge das ist schon eine ganze Weile her, aber ich weiß, dass ich die ganze Zeit Probleme mit irgend etwas hatte bis ich das so gemacht habe.
Speicher Leaks habe ich nun keine mehr, habe mir eine Funktion geschrieben in der ich zum Schluss alle dynamisch erzeugten Elemente lösche, falls eines noch offen ist.
-
OK, dann hoffe ich, daß DU das Projekt noch beherrschst (und nicht umgekehrt -)
-
Threads laufen maximal zwei, also passt das alles soweit.
Ich hoffe, dass ich das Projekt noch beherrsche. Ist mit Treibern bereits auf 100MB Quellcode angewachsen. Hatte nicht gedacht, dass es so umfangreich wird. Fehlen auch nicht mehr so viele Funktionen die integriert werden müssen.
Ein Ende ist in Sicht.
Nochmals vielen herzlichen Dank für die Hilfe.
Viele Grüße aus dem Schwabenländle
egcactus