Kommunikation von TThread mit dem Anwendungsformular
-
Hallo alle miteinander!
Da ich hier neu bin, erst einmal ein paar Dinge über mich:
- ich kenne dieses Forum bereits seit ich angefangen habe zu programmieren
und habe hier viele meiner Fragen beantworten können
- ich programmiere seit ca. 3 Jahren (mit dem Borland C++ Builder 6)
- alles was ich über C++ weiß, habe ich mir in meiner Freizeit angelesen
(daher bitte ich euch um genaue Erklärungen von Fachbegriffen, etc.)---
So, nun zum Thema:Ich arbeite gerade an einem Programm, das mit einem TThread-Array arbeitet.
(Ich habe also nur eine Unit (für den Thread) und verwende diesen Thread als Array (10 Threads gesamt))/* MainUnit.cpp - Main Form Unit */ #include "ThreadTest.h" ... // Thread Handle TTrdTest *TrdTestHandle[10]; // 10 Threads [0 bis 9] ... // Thread ID for (int TrdID = 0; TrdID < 10; ++TrdID) { // Thread starten ( Handle = Main->Handle, ThreadID, Anzahl Threads (gesamt) ) TrdTestHandle[TrdID] = new TTrdTest(Handle, TrdID+1, 10); } ... // Thread ID for (int TrdID = 0; TrdID < 10; ++TrdID) { // Thread beenden TrdTestHandle[TrdID]->Terminate(); }
/* ThreadTest.cpp - Thread Test Unit */ //--------------------------------------------------------------------------- __fastcall TTrdTest::TTrdTest(HANDLE MainHandle, int ThreadID, int ThreadCount) : TThread(False) { FreeOnTerminate = true; // Thread freigeben, wenn Terminated == true // globale Variable => Main->Handle ThreadHandle = MainHandle; // Anzahl Threads TrdCnt = ThreadCount; // Thread ID TrdID = ThreadID; } //--------------------------------------------------------------------------- ... //--------------------------------------------------------------------------- void __fastcall TTrdTest::SetThreadON(void) { // Message Parameter setzen MsgWParam = TrdID, MsgLParam = 1; // Thread AN (1) // Message senden || Thread läuft SendMessage(ThreadHandle, MYWM_TrdSearch, MsgWParam, MsgLParam); } //--------------------------------------------------------------------------- void __fastcall TTrdTest::SetThreadOFF(void) { // Message Parameter setzen MsgWParam = TrdID, MsgLParam = 0; // Thread AUS (0) // Message senden || Thread läuft SendMessage(ThreadHandle, MYWM_TrdSearch, MsgWParam, MsgLParam); } //---------------------------------------------------------------------------
Frage 1: Ich habe den Header des Threads ins Main-Formular eingebunden und das Handle für den Thread als globale Variable ebenfalls im Main-Formular deklariert.
=> Gibt es eine andere (evtl. bessere) Variante wie ich das machen kann?Frage 2: Wenn ich den Thread mit 'Terminate()' beende und ihn später erneut mit 'new' aufrufe, gibt es doch sicher i-wann einen Speicherüberlauf!?
=> Muss ich außer 'Terminate()' noch etwas machen um den Thread "sauber" zu beenden?
('delete' kann ich ja nicht nehmen, da ich sonst die globale Handle-Variable löschen würde)
Frage 3: Ich habe aus den Threads mit 'SendMessage' eine Nachricht an einen MessageHandler im Main-Formular geschickt, wenn ein Thread gestartet oder beendet wird.
=> Gibt es eine bessere Variante dem Main-Formular den Status der Threads mitzuteilen?Frage 4: Ich berechne in den Threads eine Variable und möchte diese dann ans Main-Formular schicken.
=> Wie kann ich die Variable aus den Threads ans Main-Formular schicken und diese dann anzeigen lassen?Frage 5: Wie bereits erwähnt, habe ich mir C++ in meiner Freizeit durch Beispiele und viel lesen selbst beigebracht (ohne Lehrgang etc.)
=> Gibt es an meinem Sourcecode sonst noch etwas was ich anders machen sollte?---
Ich hoffe das sind nicht zu viele Fragen.Ich freue mich schon auf euere Antworten! Bitte sagt mit ehrlich, wenn ich was anders machen muss!
-CppWannaBee
-
Nachdem ich nun den halben Tag an meinem Modelhubschraubschraub gebastelt habe
und nebenbei auf Antworten gehofft habe, habe ich mir die Zeit genommen und ein
Beispiel-Projekt geschrieben.Mit diesem Projekt möchte ich euch nochmal meine obigen Fragen näher bringen!
---
- 1 Formular
- 2 Buttons
- 1 Memo
- 50 Threads (Array)
---
Download Project (BCB 6): http://www.xup.in/dl,15136463/test.rar/
---/* MainUnit.cpp - Main Formular */ //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- #include "MainUnit.h" #include "ThreadTest.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" //--------------------------------------------------------------------------- TMain *Main; const int TrdsMax = 50; // Thread Handles TTrdTest *TrdTestHandle[TrdsMax]; // x Threads [0 bis x-1] // Anzahl aktiver Threads int TrdsActive, TrdCnt; // Zeit Start - Ende int Zeit; //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- // ########################################################################## // Main Formular OnClose Event // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::FormClose(TObject *Sender, TCloseAction &Action) { for (int i = 0; i < TrdsMax; ++i) { // Threads beenden if (TrdTestHandle) { TrdTestHandle[i]->Terminate(); TrdTestHandle[i] = NULL; } } } //--------------------------------------------------------------------------- // ########################################################################## // Buttons // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::BtnStartClick(TObject *Sender) { Zeit = ::GetTickCount(); BtnStart->Enabled = false; // Threads Aktiv = 0, Thread Anzahl = 50 TrdsActive = 0, TrdCnt = 50; for (int i = 0; i < TrdCnt; ++i) { // Threads starten (Handle = Main->Handle, TrdID = ID des Threads) TrdTestHandle[i] = new TTrdTest(Handle, i+1); } } //--------------------------------------------------------------------------- void __fastcall TMain::BtnStopClick(TObject *Sender) { Zeit = ::GetTickCount(); BtnStop->Enabled = false; for (int i = 0; i < TrdCnt; ++i) { // Threads beenden if (TrdTestHandle[i]) { TrdTestHandle[i]->Terminate(); TrdTestHandle[i] = NULL; } } } //--------------------------------------------------------------------------- // ########################################################################## // Message Handler für die Threads // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::TrdTestMsgHandler(Messages::TMessage& Msg) { if (Msg.LParam == 1) // Thread AN { TrdsActive++; Status->Lines->Add("Threads: " + String(TrdsActive) + " - TrdID: " + String(Msg.WParam)); if (TrdsActive == TrdCnt) // alle an { BtnStop->Enabled = true; Status->Lines->Add("Threads gestartet. (" + String(::GetTickCount() - Zeit) + " ms)"); } } else if (Msg.LParam == 0) // Thread AUS { TrdsActive--; Status->Lines->Add("Threads: " + String(TrdsActive) + " - TrdID: " + String(Msg.WParam)); if (TrdsActive == 0) // alle aus { BtnStart->Enabled = true; Status->Lines->Add("Threads gestoppt. (" + String(::GetTickCount() - Zeit) + " ms)"); } } else if (Msg.LParam < 0) // Zähler (pro Sekunde) { Status->Lines->Add("Thread: " + String(Msg.WParam) + " - Zähler: " + String(Msg.LParam * (-1) ) ); } } //---------------------------------------------------------------------------
/* MainUnit.h - Main Formular Header */ //--------------------------------------------------------------------------- #ifndef MainUnitH #define MainUnitH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> //--------------------------------------------------------------------------- // Windows Message für Kommunikation mit dem Thread #define MYWM_TrdTest (WM_APP + 1001) //--------------------------------------------------------------------------- class TMain : public TForm { __published: // Von der IDE verwaltete Komponenten TButton *BtnStart; TButton *BtnStop; TMemo *Status; //------------------------------------------------------------------- //--- Main Formular OnClose Event //------------------------------------------------------------------- void __fastcall FormClose(TObject *Sender, TCloseAction &Action); //------------------------------------------------------------------- //--- Buttons //------------------------------------------------------------------- void __fastcall BtnStartClick(TObject *Sender); void __fastcall BtnStopClick(TObject *Sender); private: // Anwender-Deklarationen public: // Anwender-Deklarationen __fastcall TMain(TComponent* Owner); // Funktion zum Auswerten der Messages von den Threads void __fastcall TrdTestMsgHandler(Messages::TMessage& Msg); // Message-Handler fängt Nachichten ab und führt die vordefinierte Funktion aus BEGIN_MESSAGE_MAP // PARAMETER (Name der Msg, Messages::TMessage, Funktionsname) MESSAGE_HANDLER(MYWM_TrdTest, Messages::TMessage, TrdTestMsgHandler) END_MESSAGE_MAP(TForm) }; //--------------------------------------------------------------------------- extern PACKAGE TMain *Main; //--------------------------------------------------------------------------- #endif
/* ThreadTest.cpp - Thread Test Unit */ //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- #include "ThreadTest.h" #include "MainUnit.h" //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- DWORD Pause = 5; //--------------------------------------------------------------------------- __fastcall TTrdTest::TTrdTest(HANDLE MainHandle, int ID) : TThread(False) { // Thread freigeben, wenn Terminated == true FreeOnTerminate = true; // globale Variable => Main->Handle übergeben ThreadHandle = MainHandle; // Thread ID TrdID = ID; } //--------------------------------------------------------------------------- void __fastcall TTrdTest::Execute() { try { // Main Formular sagen, dass Thread AN ist SetThreadON(); // Zähler int counter = 0; // Zeit int Zeit = ::GetTickCount(); while (!Terminated) { counter++; if (::GetTickCount() - Zeit > 1000) { // Wert des Zählers an Main Formular senden SendCounter(counter); // Zähler reset counter = 0; // Zeit reset Zeit = ::GetTickCount(); } Sleep(Pause); } // Main Formular sagen, dass Thread AUS ist SetThreadOFF(); } catch (Exception *E) { SetThreadOFF(); Application->MessageBox( String("Fehler in Thread: Trd " + String(TrdID)).c_str(), "debug:", 0+48 ); ShowException(E, NULL); } } //--------------------------------------------------------------------------- // ########################################################################## // Zähler Wert senden // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TTrdTest::SendCounter(int Wert) { SendMessage(ThreadHandle, MYWM_TrdTest, TrdID, ( Wert * (-1) ) ); } //--------------------------------------------------------------------------- // ########################################################################## // Thread Status AN / AUS // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TTrdTest::SetThreadON(void) { SendMessage(ThreadHandle, MYWM_TrdTest, TrdID, 1); } //--------------------------------------------------------------------------- void __fastcall TTrdTest::SetThreadOFF(void) { SendMessage(ThreadHandle, MYWM_TrdTest, TrdID, 0); } //---------------------------------------------------------------------------
/* ThreadTest.h - Thread Test Header */ //--------------------------------------------------------------------------- #ifndef ThreadTestH #define ThreadTestH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> //--------------------------------------------------------------------------- // Windows Message für Kommunikation mit Main-Formular #define MYWM_TrdTest (WM_APP + 1001) //--------------------------------------------------------------------------- class TTrdTest : public TThread { private: //------------------------------------------------------------------- //--- Zähler Wert senden //------------------------------------------------------------------- void __fastcall SendCounter(int Wert); //------------------------------------------------------------------- //--- Thread Status AN / AUS //------------------------------------------------------------------- void __fastcall SetThreadON(void); void __fastcall SetThreadOFF(void); protected: virtual void __fastcall Execute(void); public: __fastcall TTrdTest(HANDLE MainHandle, int ID); // Variable die im Gesamten Thread benutzt werden kann und // auch vom Hauptformular gelesen und verändert werden kann HANDLE ThreadHandle; // Thread ID int TrdID; }; //--------------------------------------------------------------------------- #endif
---
Den Wert des Zählers sende ich notgedrungen 'negativ', weil ich nicht wusste
wie ich sonst noch einen anderen Wert senden kann.
[i](wobei natürlich ein 2. Message Handler möglich wäre)*-CppWannaBee
p.s.: Ich hoffe jetzt meldet sich mal jemand!
... und bitte an meine 5 Fragen denken!
-
Hallo
Frage 1: Ich habe den Header des Threads ins Main-Formular eingebunden und das Handle für den Thread als globale Variable ebenfalls im Main-Formular deklariert.
=> Gibt es eine andere (evtl. bessere) Variante wie ich das machen kann?Der Header-Include ist in Ordnung, statt ein globalen Variablen solltest du lieber eine Membervariablen des Forms nehmen.
Frage 2: Wenn ich den Thread mit 'Terminate()' beende und ihn später erneut mit 'new' aufrufe, gibt es doch sicher i-wann einen Speicherüberlauf!?
=> Muss ich außer 'Terminate()' noch etwas machen um den Thread "sauber" zu beenden?
('delete' kann ich ja nicht nehmen, da ich sonst die globale Handle-Variable löschen würde)
Siehe die Eigenschaft TThread::FreeOnTerminate in der BCB-Hilfe.
delete löscht keine Variablen, sondern nur Instanzen im Speicher. delete darfst du für Threads nur dann aufrufen wenn Terminate aufgerufen wurde, aber FreeOnTerminate auf false stand.Frage 3: Ich habe aus den Threads mit 'SendMessage' eine Nachricht an einen MessageHandler im Main-Formular geschickt, wenn ein Thread gestartet oder beendet wird.
=> Gibt es eine bessere Variante dem Main-Formular den Status der Threads mitzuteilen?Erweitere deine Thread-Klasse um einen Zeiger auf das Form, dann kannst du vom Thread auch direkt Methoden der Form-Instanz aufrufen. Aber bitte nur Synchronisiert. Weiteres dazu im Thread-Tutorial hier im Forum.
Frage 4: Ich berechne in den Threads eine Variable und möchte diese dann ans Main-Formular schicken.
=> Wie kann ich die Variable aus den Threads ans Main-Formular schicken und diese dann anzeigen lassen?Siehe Antwort auf Frage 3.
Frage 5: Wie bereits erwähnt, habe ich mir C++ in meiner Freizeit durch Beispiele und viel lesen selbst beigebracht (ohne Lehrgang etc.)
=> Gibt es an meinem Sourcecode sonst noch etwas was ich anders machen sollte?Da ich nicht überblicke, was du mit deinem Programm genau bezweckst, kann ich dazu erstmal nichts weiter als das schon geschriebene sagen.
bis bald
akari
-
Hallo akari!
Schonmal vielen Dank für deine Antworten.
akari schrieb:
Der Header-Include ist in Ordnung, statt ein globalen Variablen solltest du lieber eine Membervariablen des Forms nehmen.
Wie kann ich denn das Handle des Threads in meiner Klasse 'TMain' unterbringen?
Ich includiere den Thread Header doch erst nach dem Main Header
Wenn ich jetzt versuche ein Handle zu erstellen, kennt er 'TTrdTest' ja nicht...akari schrieb:
Siehe die Eigenschaft TThread::FreeOnTerminate in der BCB-Hilfe.
delete löscht keine Variablen, sondern nur Instanzen im Speicher. delete darfst du für Threads nur dann aufrufen wenn Terminate aufgerufen wurde, aber FreeOnTerminate auf false stand.Gut. Damit wäre diese Frage auf jeden Fall beantwortet.
akari schrieb:
Erweitere deine Thread-Klasse um einen Zeiger auf das Form, dann kannst du vom Thread auch direkt Methoden der Form-Instanz aufrufen. Aber bitte nur Synchronisiert. Weiteres dazu im Thread-Tutorial hier im Forum.
Den Zeiger auf das Form, habe ich bereits durch den Include des Main-Form Headers. Mit 'Synchronize()' habe ich vorher auch schon gearbeitet.
Könnte ich das mit 'Synchronize()' denn so lösen?:
/* MainUnit.cpp - Main Form */ ... void __fastcall TMain::TextAnzeigen(String Text) { Status->Lines->Add(Text); } ...
/* ThreadTest.cpp - Thread Unit */ // in der Execute()-Funktion ... Text = "Hallo Welt"; // String Text = Member Variable von TTrdTest Synchronize(SendText); ... void __fastcall TTrdTest::SendText(void) { Main->TextAnzeigen(Text); } ...
Wäre das so denn richtig umgesetzt?
akari schrieb:
Da ich nicht überblicke, was du mit deinem Programm genau bezweckst, kann ich dazu erstmal nichts weiter als das schon geschriebene sagen.
Es ging mir mehr darum, ob ich an meinem Programmier-Stil etwas verändern soll.
Also ein Beispiel wären wohl globale Variablen, die ich wohl noch zu oft nehme.
Richtig?-CppWannaBee
-
Hallo
CppWannaBee schrieb:
Wie kann ich denn das Handle des Threads in meiner Klasse 'TMain' unterbringen?
Ich includiere den Thread Header doch erst nach dem Main Header
Wenn ich jetzt versuche ein Handle zu erstellen, kennt er 'TTrdTest' ja nicht...Stichwort Forward Declaration. Ich skizzire mal das Vorgehen in deinem konkretem Beispiel :
// TMain.h class TTrdTest; // Forward Declaration hinzufügen. Kein Include! class TMain { ... TTrdTest *TrdTestHandle[10]; // Aus .cpp hierher verschoben }; // TMain.cpp ... #include "TTrdTest.h" // Jetzt ist TTrdTest wie üblich verwendbar
Den Zeiger auf das Form, habe ich bereits durch den Include des Main-Form Headers.
Damit verwendest du aber wieder eine globale Variable, und davon solltest du wegkommen.
Könnte ich das mit 'Synchronize()' denn so lösen?: ...
Ja, so ist Synchronize anzuwenden.
Es ging mir mehr darum, ob ich an meinem Programmier-Stil etwas verändern soll.
Also ein Beispiel wären wohl globale Variablen, die ich wohl noch zu oft nehme.Da liese sich sicher noch ein paar Sachen verbessern, mir fehlt jetzt aber die Zeit um dein Quellcode nach konkreten Stellen zu durchsuchen. Wichtig sollte erstmal sein das der Quellcode fehlerfrei ist und du auch verstehst wie er funktioniert.
bis bald
akari
-
Hallo akari!
akari schrieb:
Stichwort Forward Declaration.
Danke für diesen prima Tipp!
(... und das super Beispiel!)
Ich hatte bisher noch nichts von Forwad Declaration gehört.akari schrieb:
Damit verwendest du aber wieder eine globale Variable, und davon solltest du wegkommen.
Da hast du Recht. Ich denke momentan leider noch in meinem alten Programmier-Schema.
Ich habe jetzt probiert im Header des Threads einen Zeiger auf das Main Form
zu erstllen. Da der Thread das Main Form auch (noch) nicht "kennt",
habe ich das mit dem neu gelernten Forward Declaration gemacht:/* TrdTest.h - Thread Test Header */ class TMain; // Forward-Deklaration des Main Form ... class TTrdTest : public TThread { private: // Zeiger auf Main Form TMain *MainForm; ...
Wenn ich dann aber mit 'Synchronize()' eine Methode des Threads aufrufe,
bekomme ich eine Zugriffsverletzung./* TrdTest.cpp - Thread Test Unit */ void __fastcall TTrdTest::Execute() { ... // Main Formular sagen, dass Thread AN ist Synchronize(SendThreadON); ... } void __fastcall TTrdTest::SendThreadON(void) { MainForm->ThreadStatus(TrdID, 1); // Thread AN // Main->ThreadStatus(TrdID, 1); /* <<< so funktioniert es */ /* aber da ist Main ja global */ } ...
Wie muss ich denn den Zeiger deklarieren, damit es funktioniert?
akari schrieb:
Ja, so ist Synchronize anzuwenden.
Klappt wirklich ohne Probleme.
Mit dem 'Synchronize()' konnte ich jetzt endlich meine 'SendMessage()'-Methoden
raushauen und alles mit beliebigen Parametern übergeben.akari schrieb:
Wichtig sollte erstmal sein das der Quellcode fehlerfrei ist und du auch verstehst wie er funktioniert.
Das hast du gut gesagt. Ich möchte den Quellcode natürlich auch verstehen.
Daher stelle ich hier auch meine Fragen. Danke für das gute Erklären!-CppWannaBee
-
Hast du MainForm denn schon Main zugewiesen? Weil sonst zeigt der Pointer ja noch ins Datennirgendwo
Zu Pointern siehe:
http://www.youtube.com/watch?v=UvoHwFvAvQEmfg
xXx
-
Hallo -=]xXx[=-!
-=]xXx[=- schrieb:
Hast du MainForm denn schon Main zugewiesen? Weil sonst zeigt der Pointer ja noch ins Datennirgendwo
Ach da lag das Problem. Jetzt wo ich mir das anschaue, springt mir der Fehler ja direkt ins Gesicht.
-=]xXx[=- schrieb:
Zu Pointern siehe:
http://www.youtube.com/watch?v=UvoHwFvAvQEHehe, sehr gutes Video. Hat er wirklich gut erklärt.
Ich danke dir für deine schnelle Hilfe!
-CppWannaBee
-
So... habe alles angepasst und das Beispiel-Projekt (siehe oben) komplett auf 'Synchronize()' umgestellt.
/* MainUnit.cpp - Main Form Unit */ //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- #include "MainUnit.h" #include "ThreadTest.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" //--------------------------------------------------------------------------- TMain *Main; //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- // ########################################################################## // Main Formular OnCreate Event // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::FormCreate(TObject *Sender) { // Maximale Anzahl Threads TrdsMax = 50; } //--------------------------------------------------------------------------- // ########################################################################## // Main Formular OnClose Event // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::FormClose(TObject *Sender, TCloseAction &Action) { for (int i = 0; i < TrdsMax; ++i) { // Threads beenden if (TrdTestHandle) { TrdTestHandle[i]->Terminate(); TrdTestHandle[i] = NULL; } } } //--------------------------------------------------------------------------- // ########################################################################## // Buttons // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::BtnStartClick(TObject *Sender) { Zeit = ::GetTickCount(); BtnStart->Enabled = false; // Threads Aktiv = 0, Thread Anzahl = 10 (Max: 50) TrdsActive = 0, TrdCnt = 10; for (int i = 0; i < TrdCnt; ++i) { // Threads starten (ID des Threads) TrdTestHandle[i] = new TTrdTest(i+1); } } //--------------------------------------------------------------------------- void __fastcall TMain::BtnStopClick(TObject *Sender) { Zeit = ::GetTickCount(); BtnStop->Enabled = false; for (int i = 0; i < TrdCnt; ++i) { // Threads beenden if (TrdTestHandle[i]) { TrdTestHandle[i]->Terminate(); TrdTestHandle[i] = NULL; } } } //--------------------------------------------------------------------------- // ########################################################################## // Thread Status - Public Methode - Aufruf nur aus den Threads! // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::ThreadStatus(int TrdID, int Status) { if (Status == 1) // Thread AN { // Anzahl aktiver Threads TrdsActive++; // Status anzeigen AnzStatus->Lines->Add("Threads: " + String(TrdsActive) + " - TrdID: " + String(TrdID)); if (TrdsActive == TrdCnt) // alle Threads sind AN { BtnStop->Enabled = true; AnzStatus->Lines->Add("---"); AnzStatus->Lines->Add("Threads gestartet. (" + String(::GetTickCount() - Zeit) + " ms)"); AnzStatus->Lines->Add("---"); for (int i = 0; i < TrdCnt; ++i) { AnzStatus->Lines->Add("Thread Counter " + String(i) + ": 0"); } } } else if (Status == 0) // Thread AUS { if (TrdsActive == TrdCnt) { AnzStatus->Lines->Add("---"); } // Anzahl aktiver Threads TrdsActive--; // Status anzeigen AnzStatus->Lines->Add("Threads: " + String(TrdsActive) + " - TrdID: " + String(TrdID)); if (TrdsActive == 0) // alle Threads sind AUS { BtnStart->Enabled = true; AnzStatus->Lines->Add("---"); AnzStatus->Lines->Add("Threads gestoppt. (" + String(::GetTickCount() - Zeit) + " ms)"); AnzStatus->Lines->Add("---"); } } } //--------------------------------------------------------------------------- // ########################################################################## // Thread Counter - Public Methode - Aufruf nur aus den Threads! // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::ThreadCounter(int TrdID, int Counter) { // Wert des Zählers anzeigen (Wert = Durchläufe pro Sekunde) AnzStatus->Lines->Strings[(TrdCnt - 1) + 3 + TrdID] = "Thread Counter " + String(TrdID) + ": " + String(Counter); } //--------------------------------------------------------------------------- // ########################################################################## // Thread ERROR - Public Methode - Aufruf nur aus den Threads! // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TMain::ThreadError(int TrdID, String Error) { AnzStatus->Lines->Add("---"); // Error anzeigen AnzStatus->Lines->Add("Thread: " + String(TrdID) + " - Error: " + Error ); AnzStatus->Lines->Add("---"); } //---------------------------------------------------------------------------
/* MainUnit.h - Main Form Header */ //--------------------------------------------------------------------------- #ifndef MainUnitH #define MainUnitH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> //--------------------------------------------------------------------------- class TTrdTest; // Forward-Deklaration des Threads //--------------------------------------------------------------------------- class TMain : public TForm { __published: // Von der IDE verwaltete Komponenten TButton *BtnStart; TButton *BtnStop; TMemo *AnzStatus; //------------------------------------------------------------------- //--- Main Formular OnCreate Event //------------------------------------------------------------------- void __fastcall FormCreate(TObject *Sender); //------------------------------------------------------------------- //--- Main Formular OnClose Event //------------------------------------------------------------------- void __fastcall FormClose(TObject *Sender, TCloseAction &Action); //------------------------------------------------------------------- //--- Buttons //------------------------------------------------------------------- void __fastcall BtnStartClick(TObject *Sender); void __fastcall BtnStopClick(TObject *Sender); //--------------------------------------------------------------------------- private: /* Variablen-Deklarationen */ // Thread Handles TTrdTest *TrdTestHandle[50]; // maximal 50 Threads // Anzahl aktiver Threads, Threads maximal int TrdsActive, TrdCnt, TrdsMax; // Zeit Start - Ende int Zeit; //--------------------------------------------------------------------------- public: // Methoden-Deklarationen // Konstruktor __fastcall TMain(TComponent* Owner); // Thread AN / AUS - Aufruf nur aus den Threads! void __fastcall ThreadStatus(int TrdID, int Status); // Werte aus den Threads - Aufruf nur aus den Threads! void __fastcall ThreadCounter(int TrdID, int Counter); // Thread ERROR - Aufruf nur aus den Threads! void __fastcall ThreadError(int TrdID, String Error); }; //--------------------------------------------------------------------------- extern PACKAGE TMain *Main; //--------------------------------------------------------------------------- #endif
/* ThreadTest.cpp - Thread Test Unit */ //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- #include "ThreadTest.h" #include "MainUnit.h" //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- __fastcall TTrdTest::TTrdTest(int ID) : TThread(False) { // Pause Pause = 5; // 5 ms // Thread freigeben, wenn Terminated == true FreeOnTerminate = true; // Thread ID TrdID = ID; // Fehler im Thread Error = ""; // Zeiger auf Main Form MainForm = Main; } //--------------------------------------------------------------------------- void __fastcall TTrdTest::Execute() { try { // Main Formular sagen, dass Thread AN ist Synchronize(SendThreadON); // counter - Durchläufe pro Sekunde counter = 0; // Zeit int Zeit = ::GetTickCount(); while (!Terminated) { counter++; if (::GetTickCount() - Zeit > 1000) { // Wert des Zählers an Main Formular senden Synchronize(SendCounter); // Zähler reset counter = 0; // Zeit reset Zeit = ::GetTickCount(); } Sleep(Pause); } // Main Formular sagen, dass Thread AUS ist Synchronize(SendThreadOFF); } catch (Exception *E) { // Exception in String speichern Error = E->Message; // Main Formular sagen, dass in Thread ein Fehler aufgetreten ist Synchronize(SendThreadError); // Main Formular sagen, dass Thread AUS ist Synchronize(SendThreadOFF); } } //--------------------------------------------------------------------------- // ########################################################################## // Zähler Wert senden // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TTrdTest::SendCounter(void) { MainForm->ThreadCounter(TrdID, counter); } //--------------------------------------------------------------------------- // ########################################################################## // Thread Status AN / AUS // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TTrdTest::SendThreadON(void) { MainForm->ThreadStatus(TrdID, 1); // Thread AN } //--------------------------------------------------------------------------- void __fastcall TTrdTest::SendThreadOFF(void) { MainForm->ThreadStatus(TrdID, 0); // Thread AUS } //--------------------------------------------------------------------------- // ########################################################################## // Thread Status ERROR // ########################################################################## //--------------------------------------------------------------------------- void __fastcall TTrdTest::SendThreadError(void) { MainForm->ThreadError(TrdID, Error); // Thread ERROR } //---------------------------------------------------------------------------
/* ThreadTest.h - Thread Test Header */ //--------------------------------------------------------------------------- #ifndef ThreadTestH #define ThreadTestH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> //--------------------------------------------------------------------------- class TMain; // Forward-Deklaration des Main Form //--------------------------------------------------------------------------- class TTrdTest : public TThread { private: // Pause DWORD Pause; // Thread ID int TrdID; // counter - Durchläufe pro Sekunde int counter; // Fehler im Thread String Error; // Zeiger auf Main Form TMain *MainForm; //------------------------------------------------------------------- //--- Zähler Wert senden //------------------------------------------------------------------- void __fastcall SendCounter(void); //------------------------------------------------------------------- //--- Thread Status AN / AUS //------------------------------------------------------------------- void __fastcall SendThreadON(void); void __fastcall SendThreadOFF(void); //------------------------------------------------------------------- //--- Thread Status ERROR //------------------------------------------------------------------- void __fastcall SendThreadError(void); //--------------------------------------------------------------------------- protected: virtual void __fastcall Execute(void); //--------------------------------------------------------------------------- public: __fastcall TTrdTest(int ID); }; //--------------------------------------------------------------------------- #endif
Ein paar kleine Fragen habe ich allerdings noch.
Frage 1: Sollte ich den Wert der Member-Variable [i](int TrdsMax)* im Konstruktor von TMain setzen oder so wie bei mir im 'OnCreate'?
=> Welchen Unterschied macht das dann?Frage 2: Sagen wir mal der Thread beendet die Execute()-Methode mal wegen eines Fehlers.
Dann wird dieser Fehler mit 'catch()' abgefangen.
=> Wird der Thread automatisch "terminiert" (Terminated == true), wenn er die Execute()-Methode verlässt?
=> Oder muss ich im 'catch()' noch 'Terminate()' schreiben?---
Ich möchte nochmal akari für seine kompetente und ausführliche Hilfe danken!
Dank geht natürlich auch an -=]xXx[=- für den kleinen Denkanstoß!-CppWannaBee
-
Hallo
1. Im Konstruktor. Sowohl OnCreate als auch OnDestroy solltem im C++ Builder nicht verwendet werden, das sind Überbleibsel aus der Delphi-Herkunft. begründung findest du mit der Forumssuche.
2. Ob bei einer Exception der Thread noch automatisch gelöscht wird, weiß ich jetzt nicht genau. Solange es aber nur um das äußerste catch geht, ist das kein Problem, da mit dem Verlassen vom Programm sowieso aller Speicher automatisch freigegeben wird.
3. Bitte Poste keine ewig langen Quellcode-Auszüge, nur zur Fragestellung relevante Stellen. Wenn du ein ganzes Projekt verfügbar machen willst, dann wie schon gemacht über einen externen Anbieter für Dateiaustausch.
bis bald
akari
-
Hallo akari!
akari schrieb:
1. Im Konstruktor. Sowohl OnCreate als auch OnDestroy solltem im C++ Builder nicht verwendet werden, das sind Überbleibsel aus der Delphi-Herkunft. begründung findest du mit der Forumssuche.
Danke für den Hinweis. Werde es ab sofort immer beachten.
Hier der Link: Don't use OnCreate and OnDestroy, use C++ constructors and destructors insteadakari schrieb:
2. Ob bei einer Exception der Thread noch automatisch gelöscht wird, weiß ich jetzt nicht genau. Solange es aber nur um das äußerste catch geht, ist das kein Problem, da mit dem Verlassen vom Programm sowieso aller Speicher automatisch freigegeben wird.
Also ich bin ja der Meinung, dass der Thread nicht automatisch durch das Verlassen der 'Execute()'-Methode terminiert wird.
=> Das werd ich mal ausprobieren mit 'OnTerminate'akari schrieb:
3. Bitte Poste keine ewig langen Quellcode-Auszüge, nur zur Fragestellung relevante Stellen. Wenn du ein ganzes Projekt verfügbar machen willst, dann wie schon gemacht über einen externen Anbieter für Dateiaustausch.
Ok, tut mir Leid.
Ich war nur der Meinung das der gesamte Source mehr auf eventuelle Fehler hinweisen kann.
Sollte ich mal wieder nen längeren Source haben, werd ich es über Free-FileHoster machen.---
Nochmnals vielen Dank für deine Hilfe, akari!-CppWannaBee