#1 24. April 2009 Zuletzt von einem Moderator bearbeitet: 14. April 2017 [Borland] Threads steuern und Werte anzeigen n'abend! Ich arbeite nun schon seit ca. 3 Jahren mit dem Borland C++ Builder und seit Version 6 auch mit Threads. Da ich mir das meiste selbst beigebracht habe, bzw. vieles mit Hilfe von Google und einigen hilfsbereiten Programmierern gelernt habe, mache ich noch viele kleine Fehler. Meine Programme laufen zwar, aber ich bin mir nie sicher ob sie 100% "sauber" programmiert sind. Daher hab ich gerade mal ein einfaches Programm zur Steuerung von Threads und zur Anzeige von Werten, die in den Threads berechnet werden, geschrieben. Ich bitte nun die erfahrenen Programmierer unter euch, meinen Code mal anzuschauen und mögliche "unsaubere" Stellen zu verbessern - am Besten mit Begründung. Evtl. gibt es auch Dinge, die ich einfacher lösen könnte - also bitte erwähnen ^^ SourceCode: (alte Version) Spoiler - 1 Formular | 3 Threads - 1 Statusanzeige | 3 Anzeigen für die Werte - 2 Buttons (Start / Stop) Formular (MainUnit.cpp): Code: //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "MainUnit.h" #include "Thread01.h" #include "Thread02.h" #include "Thread03.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TMain *Main; // Thread Handles TTrd01 *Trd01Handle; TTrd02 *Trd02Handle; TTrd03 *Trd03Handle; // Thread Status int TrdCnt = 3; // Anzahl Threads (gesamt) int TrdsArray[4]; // [0] = Anzahl aktiver Threads; [x] = Status an/aus (1/-1) //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Main-Formular-Funktionen //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TMain::FormClose(TObject *Sender, TCloseAction &Action) { // Thread-Handles prüfen und ggf. beenden if (Trd01Handle) Trd01Handle->Terminate(); if (Trd02Handle) Trd02Handle->Terminate(); if (Trd03Handle) Trd03Handle->Terminate(); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Control-Buttons || Threads steuern //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TMain::BtnStartClick(TObject *Sender) { BtnStart->Enabled = false; // Status anzeigen AnzStatus->Caption = " Threads werden gestartet... "; AnzStatus->SetFocus(); AnzTrd01->Caption = "0"; AnzTrd02->Caption = "0"; AnzTrd03->Caption = "0"; // Thread Status TrdsArray[0] = 0; TrdsArray[1] = -1; TrdsArray[2] = -1; TrdsArray[3] = -1; // Threads starten | Handle = Main->Handle Trd01Handle = new TTrd01(Handle); Trd02Handle = new TTrd02(Handle); Trd03Handle = new TTrd03(Handle); BtnStop->Enabled = true; } //--------------------------------------------------------------------------- void __fastcall TMain::BtnStopClick(TObject *Sender) { BtnStop->Enabled = false; // Status anzeigen AnzStatus->Caption = " Threads werden beendet..."; AnzStatus->SetFocus(); // Thread-Handle prüfen if (Trd01Handle) Trd01Handle->Terminate(); if (Trd02Handle) Trd02Handle->Terminate(); if (Trd03Handle) Trd03Handle->Terminate(); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Message Handler für die Threads || public //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TMain::TrdStatus(int TrdID, int TrdMode) { // Anzahl aktiver Threads TrdsArray[0] = TrdsArray[0] + TrdMode; // Status für Thread 'TrdID' setzen (an/aus) TrdsArray[TrdID] = TrdMode; // Statusanzeige anpassen if (TrdsArray[0] == TrdCnt) { AnzStatus->Caption = " Threads gestartet. "; } else if (TrdsArray[0] == 0) { AnzStatus->Caption = " Threads beendet. "; BtnStart->Enabled = true; } } //--------------------------------------------------------------------------- void __fastcall TMain::Trd01MsgHandler(Messages::TMessage& Msg) { if ((Msg.WParam == 1 || Msg.WParam == -1) && Msg.LParam == 0) // Threads an/aus { TrdStatus(int(01), Msg.WParam); } } //--------------------------------------------------------------------------- void __fastcall TMain::Trd02MsgHandler(Messages::TMessage& Msg) { if ((Msg.WParam == 1 || Msg.WParam == -1) && Msg.LParam == 0) // Threads an/aus { TrdStatus(int(02), Msg.WParam); } } //--------------------------------------------------------------------------- void __fastcall TMain::Trd03MsgHandler(Messages::TMessage& Msg) { if ((Msg.WParam == 1 || Msg.WParam == -1) && Msg.LParam == 0) // Threads an/aus { TrdStatus(int(03), Msg.WParam); } } //--------------------------------------------------------------------------- Header (MainUnit.h): Code: //--------------------------------------------------------------------------- #ifndef MainUnitH #define MainUnitH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <ExtCtrls.hpp> //--------------------------------------------------------------------------- // Windows Message für Kommunikation mit den Threads #define MYWM_Trd01 (WM_APP + 1001) #define MYWM_Trd02 (WM_APP + 1002) #define MYWM_Trd03 (WM_APP + 1003) //--------------------------------------------------------------------------- class TMain : public TForm { __published: // Von der IDE verwaltete Komponenten TButton *BtnStart; TButton *BtnStop; TPanel *AnzStatus; TPanel *AnzTrd01; TPanel *AnzTrd02; TPanel *AnzTrd03; //--- // Main-Formular-Funktionen //--- void __fastcall FormClose(TObject *Sender, TCloseAction &Action); //--- // Control-Buttons || Threads steuern //--- 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 TrdStatus(int TrdID, int TrdMode); void __fastcall Trd01MsgHandler(Messages::TMessage& Msg); void __fastcall Trd02MsgHandler(Messages::TMessage& Msg); void __fastcall Trd03MsgHandler(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_Trd01, Messages::TMessage, Trd01MsgHandler) MESSAGE_HANDLER(MYWM_Trd02, Messages::TMessage, Trd02MsgHandler) MESSAGE_HANDLER(MYWM_Trd03, Messages::TMessage, Trd03MsgHandler) END_MESSAGE_MAP(TForm) }; //--------------------------------------------------------------------------- extern PACKAGE TMain *Main; //--------------------------------------------------------------------------- #endif Trd01 (Thread01.cpp): Code: //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Thread01.h" #include "MainUnit.h" //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- // Pause in Millisekunden für 'Sleep()' DWORD Pause = 5; // 5 ms // Message Parameter int MsgWParam, MsgLParam; // Counter int counter; //--------------------------------------------------------------------------- __fastcall TTrd01::TTrd01(HANDLE MainHandle) : TThread(False) { FreeOnTerminate = true; // Thread freigeben, wenn Terminated == true // globale Variable => Main->Handle übergeben ThreadHandle = MainHandle; } //--------------------------------------------------------------------------- void __fastcall TTrd01::Execute() { // Message Parameter setzen MsgWParam = 1, MsgLParam = 0; // Message senden || Thread läuft SendMessage(ThreadHandle, MYWM_Trd01, MsgWParam, MsgLParam); try { // Counter counter = 0; while (!Terminated) { // Counter + 1 counter++; // Counter in Main-Formular anzeigen Synchronize(ShowCounter); Sleep(Pause); } } catch (Exception *E) { Application->MessageBoxA( "Fehler in Thread: Trd01", "debug:", 0+48 ); ShowException(E, NULL); } // Message Parameter setzen MsgWParam = -1, MsgLParam = 0; // Message senden || Thread beendet SendMessage(ThreadHandle, MYWM_Trd01, MsgWParam, MsgLParam); } //--------------------------------------------------------------------------- void __fastcall TTrd01::ShowCounter(void) { Main->AnzTrd01->Caption = String(counter); } //--------------------------------------------------------------------------- Trd01 Header (Thread01.h): Code: //--------------------------------------------------------------------------- #ifndef Thread01H #define Thread01H //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> //--------------------------------------------------------------------------- // Windows Message für Kommunikation mit Main-Formular #define MYWM_Trd01 (WM_APP + 1001) //--------------------------------------------------------------------------- class TTrd01 : public TThread { private: void __fastcall ShowCounter(void); protected: virtual void __fastcall Execute(void); public: __fastcall TTrd01(HANDLE MainHandle); // Variable die im Gesamten Thread benutzt werden kann und // auch vom Hauptformular gelesen und verändert werden kann HANDLE ThreadHandle; }; //--------------------------------------------------------------------------- #endif Thread 01, 02 und 03 sind identisch! Habe nur 01 durch 02 bzw. 03 ersetzt! Beispiel: #define MYWM_Trd01 (WM_APP + 1001) // 1002 // 1003 --- Falls jemand wissen will, was das Ding nun macht: Download via xup! SourceCode: (neue Version - 26.04. - 14:30 Uhr) Spoiler - 1 Formular | Array für Threads (1 - 10) - 1 Statusanzeige (Memo) - 2 Buttons (Start / Stop) - 1 ComboBox zur Auswahl der Thread-Anzahl Formular (MainUnit.cpp): Code: //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "MainUnit.h" #include "ThreadCount.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TMain *Main; // Thread Werte int TrdCnt = 0; // Anzahl Threads (gesamt) int TrdsActive; // Anzahl aktiver Threads // Thread Handles TTrdCnt *TrdCntHandle[10]; // 10 Threads || 0 - 9 //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Main-Formular-Funktionen //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TMain::FormClose(TObject *Sender, TCloseAction &Action) { // Thread-Handles prüfen und ggf. beenden for (int i = 0; i < TrdCnt; i++) { if (TrdCntHandle[i]) TrdCntHandle[i]->Terminate(); } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Control-Buttons || Threads steuern //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TMain::BtnStartClick(TObject *Sender) { BtnStart->Enabled = false; Main->SetFocus(); // Anzahl Threads || ComboBox MainFormular TrdCnt = StrToInt(CBoxAnzahl->Text); // Anzahl aktiver Threads || reset TrdsActive = 0; // Status anzeigen AnzStatus->Clear(); AnzStatus->Lines->Add("Threads werden gestartet... "); // Threads starten | Handle = Main->Handle | TrdID for (int i = 0; i < TrdCnt; i++) { TrdCntHandle[i] = new TTrdCnt(Handle, i+1); } BtnStop->Enabled = true; } //--------------------------------------------------------------------------- void __fastcall TMain::BtnStopClick(TObject *Sender) { BtnStop->Enabled = false; Main->SetFocus(); // Status anzeigen AnzStatus->Lines->Add("Threads werden beendet... "); // Thread-Handle prüfen for (int i = 0; i < TrdCnt; i++) { if (TrdCntHandle[i]) TrdCntHandle[i]->Terminate(); } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Message Handler für die Threads || public //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TMain::TrdStatus(int TrdID, int TrdMode) { if (TrdMode == 1) { TrdsActive++; } else { TrdsActive--; } // Statusanzeige anpassen if (TrdsActive == TrdCnt) { AnzStatus->Lines->Add(String(TrdCnt) + " Threads gestartet."); } else if (TrdsActive == 0) { AnzStatus->Lines->Add("Threads beendet."); BtnStart->Enabled = true; } } //--------------------------------------------------------------------------- void __fastcall TMain::TrdCntMsgHandler(Messages::TMessage& Msg) { if (Msg.LParam == 1 || Msg.LParam == 0) // Threads an/aus { TrdStatus(Msg.WParam, Msg.LParam); } } //--------------------------------------------------------------------------- Header (MainUnit.h): Code: //--------------------------------------------------------------------------- #ifndef MainUnitH #define MainUnitH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <ExtCtrls.hpp> //--------------------------------------------------------------------------- // Windows Message für Kommunikation mit den Threads #define MYWM_TrdCnt (WM_APP + 1001) //--------------------------------------------------------------------------- class TMain : public TForm { __published: // Von der IDE verwaltete Komponenten TButton *BtnStart; TButton *BtnStop; TComboBox *CBoxAnzahl; TMemo *AnzStatus; //--- // Main-Formular-Funktionen //--- void __fastcall FormClose(TObject *Sender, TCloseAction &Action); //--- // Control-Buttons || Threads steuern //--- 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 TrdStatus(int TrdID, int TrdMode); void __fastcall TrdCntMsgHandler(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_TrdCnt, Messages::TMessage, TrdCntMsgHandler) END_MESSAGE_MAP(TForm) }; //--------------------------------------------------------------------------- extern PACKAGE TMain *Main; //--------------------------------------------------------------------------- #endif Thread Count (ThreadCount.cpp): (atm max. 10 Threads - identisch bis auf TrdID) Code: //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "ThreadCount.h" #include "MainUnit.h" //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- __fastcall TTrdCnt::TTrdCnt(HANDLE MainHandle, int ThreadID) : TThread(False) { FreeOnTerminate = true; // Thread freigeben, wenn Terminated == true // globale Variable => Main->Handle übergeben ThreadHandle = MainHandle; // Thread ID zuweisen TrdID = ThreadID; } //--------------------------------------------------------------------------- void __fastcall TTrdCnt::Execute() { Synchronize(ThreadON); try { // Counter int cntnow = 0; counter = 0; // Pause Pause = 5; // 5 ms while (!Terminated) { // Counter + 1 cntnow++; counter++; if (cntnow == 1000) { cntnow = 0; // Status anzeigen Synchronize(ShowStatus); } Sleep(Pause); } } catch (Exception *E) { Application->MessageBoxA( String("Fehler in Thread: TrdCnt - ID: " + String(TrdID)).c_str(), "debug:", 0+48 ); ShowException(E, NULL); } Synchronize(ThreadOFF); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--- Werte anzeigen || Main Formular //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TTrdCnt::ThreadON(void) { // Message Parameter setzen MsgWParam = TrdID, MsgLParam = 1; // Thread AN (1) // Message senden || Thread läuft SendMessage(ThreadHandle, MYWM_TrdCnt, MsgWParam, MsgLParam); } //--------------------------------------------------------------------------- void __fastcall TTrdCnt::ThreadOFF(void) { // Message Parameter setzen MsgWParam = TrdID, MsgLParam = 0; // Thread AUS (0) // Message senden || Thread läuft SendMessage(ThreadHandle, MYWM_TrdCnt, MsgWParam, MsgLParam); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--- Werte anzeigen || Main Formular //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TTrdCnt::ShowStatus(void) { // TrdID für Anzeige formatieren String ID = String(TrdID); if (TrdID < 10) { ID = "0" + ID; } Main->AnzStatus->Lines->Add("Thread " + ID + ": " + String(counter)); } //--------------------------------------------------------------------------- Header (ThreadCount.h): Code: //--------------------------------------------------------------------------- #ifndef ThreadCountH #define ThreadCountH //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> //--------------------------------------------------------------------------- // Windows Message für Kommunikation mit Main-Formular #define MYWM_TrdCnt (WM_APP + 1001) //--------------------------------------------------------------------------- class TTrdCnt : public TThread { private: // Pause in Millisekunden für 'Sleep()' DWORD Pause; // Counter int counter; // Message Parameter int MsgWParam, MsgLParam; //--- // Thread Status //--- void __fastcall ThreadON(void); void __fastcall ThreadOFF(void); //--- // Funktionen zur Anzeige //--- void __fastcall ShowStatus(void); protected: virtual void __fastcall Execute(void); public: __fastcall TTrdCnt(HANDLE MainHandle, int ThreadID); // Variable die im Gesamten Thread benutzt werden kann und // auch vom Hauptformular gelesen und verändert werden kann HANDLE ThreadHandle; int TrdID; }; //--------------------------------------------------------------------------- #endif Download der neuen Version: Download via xup! so... jetzt wäre ich sehr dankbar über einige Einschätzungen von erfahrenen Programmierern! greetz + Multi-Zitat Zitieren
#2 26. April 2009 AW: [Borland] Threads steuern und Werte anzeigen Verwende doch ein Array für deine 3 identischen Threads. Dann kannst du später auch die Anzahl Threads verringern oder vergrößern - ohne Probleme. Des weiteren bräuchtest du keine 3 gleichen Klassen, keine gleichen Funktionen, ... Globale Variablen sind fast immer unschön. Du kannst nicht kontrollieren, wer sie verwendet und/oder verändert. Das macht das Fehler suchen in größeren Programmen sehr schwer. [HANDLE ThreadHandle;] Die Pause in Millisekunden für den Sleep würde ich in die Header-Datei packen. Die Header-Datei ist das, was man später "von aussen" als Schalter ansehen kann, und der Sleep könnte durchaus als variable Einstellung gesehen werden. Mehr hab ich jetz grad nicht an Verbesserungsvorschlägen. + Multi-Zitat Zitieren
#3 26. April 2009 AW: [Borland] Threads steuern und Werte anzeigen Nach mehrmaligen Durchlesen des Codes ist mir eigentlich nur eins Aufgefallen, dass Fehler verursachen kann und zwar ist es die globale Variable "Pause". Diese wird bei deinem Projekt zwar nur ausgelesen, jedoch wenn du bei größeren Projekten direkt von einem Thread auf eine gemeinsam genutzte Variable liest der schreibst wird es Früher oder Später zu einer Zugriffsverletzung führen. Deswegen solltest du beim Zugriff von gemeinsam genutzten Variablen auch die Funktion Synchronize() benutzten: im weiteren Sinne in diese die benötigten Variablen in der Thradinstanz speichern bzw. aus dieser auslesen. Beim Threadprogrammierung sollte darauf geachtet werden, dass der Zugriff auf globale Variablen immer nur durch Synchronize geschieht. SendMessage ist immer zur Kommunikation zwischen Thread gut, jedoch sollte man keine Integer-Pointer übergeben die wiederum von allen Threads benutzt werden. Zum Zählen wie oft die ein und der Selbe Instanztyp nun als Thread gestartet ist könnte man auch in Objektdefinition nun eine statische Variable definieren die beim erstellen einer Instanz erhöht bzw. beim Beenden einer wieder verringert wird, hierbei handelt es sich mehr oder weniger auch um ein globale Variable auf die auch mit Synchronize() zugegriffen werden sollte. Mfg Rushh0ur + Multi-Zitat Zitieren
#4 26. April 2009 AW: [Borland] Threads steuern und Werte anzeigen Gute Idee! Werde ich so machen! :] --Edit-- ... so: neuer SourceCode ist im 1. beitrag zu finden Wenn ich das Handle aber nich als globale Variable verwenden kann, wie denn dann? ?( Gleiches Thema: ich sende einen Wert aus dem MainFormular an den Thread... - Wie kann ich diesen Wert dann in JEDER Funktion des Threads verwenden, wenn er nicht global ist? Schonmal vielen Dank für die Idee mit dem Array für die Threads! + Multi-Zitat Zitieren
#5 30. April 2009 AW: [Borland] Threads steuern und Werte anzeigen Du könntest den Wert z.B. als Klassenvariable speichern. Dann kannst du ihn innerhalb der Klasse (deine Threads sind ja Klassen) verwenden. + Multi-Zitat Zitieren
#6 30. April 2009 AW: [Borland] Threads steuern und Werte anzeigen joa, also das die Threads Klassen sind, ist klar. Die Variablen einfach im Header (in der Klasse) zu definieren würde natürlich auch gehen. Aber genau das habe ich ja eben mit dem 'ThreadHandle' gemacht. Da meinte Tanya ja das es nich die beste Lösung wäre. Gibt es denn noch eine andere (bessere) Lösung? - Funktioniert zwar alles perfekt, aber offensichtlich ja nicht 100% 'sauber' programmiert greetz + Multi-Zitat Zitieren