Announcement

Collapse
No announcement yet.

Kommunikation von TThread mit dem Anwendungsformular

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Kommunikation von TThread mit dem Anwendungsformular

    Hallo alle miteinander!

    Da ich hier neu bin, erst einmal ein paar Dinge über mich:
    - 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 momentan in Verwendung - maximal 50 könnten es sein)

    Code:
     1 Formular  (Main)
     2 Buttons   (BtnStart, BtnStop)
     1 Memo      (AnzStatus)
    10 Threads   (TTrdTest; 10 Threads aktiv; maximal 50 Threads möglich)
    Bevor ich jetzt größere Anwendungen mit diesem Schema programmiere,
    würde ich gern euere Meinung zu meinem Programmier-Stil bzw. dem Programm an sich hören.

    (damit möchte ich verhindern, dass ich später viel Aufwand beim Fehlerausbessern habe)

    Hier ein Beispiel-Projekt, was ich geschrieben habe um mein Thread-Schema zu demonstrieren:
    (programmiert in C++ mit dem Borland C++ Builder 6)

    MainUnit.cpp - Main Form Unit:
    Code:
    /* 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)
    {
      // 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:
    Code:
    /* 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 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
    ---
    Beitrag zu lang - im nächsten Beitrag gehts weiter
    ---
    Zuletzt editiert von CppWannaBee; 24.10.2009, 13:27.

  • #2
    Kommunikation von TThread mit dem Anwendungsformular

    ThreadTest.cpp - Thread Test Unit:
    Code:
    /* 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:
    Code:
    /* 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
    ---
    Wäre super, wenn ihr euch den Source mal anschauen könntet und mir dann
    sagt, ob ich i-was daran ändern sollte bevor ich größere Dinge damit programmiere.

    ---
    Ich habe auch noch eine kleine Frage bezüglich der 'Execute()'-Methode des Threads:
    Wenn ich die 'Execute()'-Methode verlasse, ohne das das Main Form 'Terminate()' aufgerufen hat,
    wird der Thread dann trotzdem beim Verlassen der 'Execute()'-Methode "terminiert" (Terminated == true) ?

    ---

    Ich freue mich schon auf euere Antworten! Bitte sagt mit ehrlich, wenn ich was anders machen muss!


    --Edit--
    Ich habe gerade einen nützlichen Link gefunden, der mir einen kleinen 'Fehler' in meinem Source gezeigt hat.
    Link: Don't use OnCreate and OnDestroy, use C++ constructors and destructors instead

    Habe meinen 'Fehler' im Source umgeändert / ausgebessert.



    -CppWannaBee
    Zuletzt editiert von CppWannaBee; 24.10.2009, 13:26.

    Comment


    • #3
      Wenn hier keiner antwortet, gehe ich jetzt mal davon aus, dass niemand einen
      Fehler im Source gefunden hat... !?

      Oder hattet ihr noch keine Zeit mir zu antworten?
      Oder... ihr wisst es evtl. auch nicht besser als ich und wollt nix falsches sagen.


      Naja, wär nett wenn jemand mal was dazu sagen könnte.



      -CppWannaBee

      Comment


      • #4
        Statt vieler Synchronize-Aufrufe würde ich mir überlegen ob du nicht per WM_xx-Messages Statusmeldungen an das Hauptprogramm sendest. Das würde weniger Blockierungen verursachen.

        Comment


        • #5
          Originally posted by Bernhard Geyer View Post
          Statt vieler Synchronize-Aufrufe würde ich mir überlegen ob du nicht per WM_xx-Messages Statusmeldungen an das Hauptprogramm sendest.
          Hallo Bernhard Geyer!

          Die Variante mit den WindowsMessages habe ich vorher bereits genutzt,
          jedoch bin ich der Meinung, dass Synchronize() schneller arbeitet.

          Ich habe vor ein paar Monaten (wieder) angefangen mit Multi-Threading zu arbeiten
          und wollte diesmal möglichst wenig Fehler machen, da meine Anwendungen
          dann teilweise auch über längere Zeiträume laufen sollen.

          Also habe ich ein paar Tests gemacht mit 2 identischen Projekten.
          - Projekt 1 mit Synchronize()
          - Projekt 2 mit WindowsMessage (SendMessage)

          Resultat:
          - pro Minute Laufzeit des Programmes, war das 2. Projekt 31 ms langsamer.

          Ich kann mir das höchstens so vorstellen das der selbst erstellte MessageHandler
          langsamer arbeitet als Synchronize()...

          Originally posted by Bernhard Geyer View Post
          Das würde weniger Blockierungen verursachen.
          In diesem Punkt muss ich dir allerdings Recht geben.
          Mit SendMessage wird der Thread nicht angehalten, sondern läuft ohne
          Unterbrechung weiter und berechnet alles so wie er soll.

          Dafür "stauen" sich aber die Messages im MessageHandler an.
          Das heißt dann für mich: Der Thread wird zwar nicht unterbrochen, aber
          die berechneten Daten kommen mit Verzögerung erst da an, wo sie hin sollen.

          Wenn ich das oben erwähnte 2. Projekt 1 Stunde lang laufen lies,
          und dann die Threads beendet habe, war der MessageHandler 2 Sekunden später
          immernoch damit beschäft die Messages auszuwerten.

          Ich kann mir zwar nicht wirklich erklären warum das so ist, aber solange
          es so ist, kann und werde ich nicht mit WindowsMessages arbeiten.

          ---
          Ein anderes Thema wäre dann noch:
          Wie übertrage ich die Inhalte meiner Berechnungen (int, AnsiString, etc.)
          ohne Synchronize() an mein MainForm?

          Ich sehe da bisher keine andere Möglichkeit, als dies mit Synchronize() zu erledigen!



          -CppWannaBee

          Comment


          • #6
            Nicht SendMessage sondern PostMessage verwenden. Und das Hauptprogramm prüft ob eine Aktualsierung nötig ist bzw. ob die aktualisierung schon jetzt nötig ist (z.B. Progressbar alle 10 ms zu aktualisieren ist unnötig -> Alle 500 ms reicht). Statusbar nur alle 1 Sekunde, ...

            Comment

            Working...
            X