VCL und Threadsicherheit



  • Habe jetzt mal ein relativ einfaches Test-Programm geschrieben um mir die Verwendung von 'TTimer' anzuschauen:
    - 1 Formular, 1 TPanel zum Anzeigen der Laufzeit
    - 2 TTimer (Laufzeit aller 1000 ms; prüfen ob ein Sound abgespielt werden soll aller 25 ms)
    - 1 TMediaPlayer zum Abspielen der Sounds

    // 'UnitMain.cpp' - class TMain - Hauptformular
    //---------------------------------------------------------------------------
    #include <vcl.h>        // Visual Command Library (VCL)
    #pragma hdrstop
    //---------------------------------------------------------------------------
    #include "UnitMain.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    //---------------------------------------------------------------------------
    TMain *Main;
    
    //---------------------------------------------------------------------------
    // Projekt-Start:  16.08.2013 - 13:15 Uhr
    //---------------------------------------------------------------------------
    // letztes Update: 16.08.2013 - 15:40 Uhr
    //---------------------------------------------------------------------------
    
    // ##########################################################################      ###  Konstruktor
    // ### Konstruktor - deklarierte Variablen initialisieren                 ###
    // ##########################################################################
    
    //---------------------------------------------------------------------------
    __fastcall TMain::TMain(TComponent* Owner)
            : TForm(Owner)
    {
      // Programmpfad und ini-Datei
      Programmpfad = ExtractFilePath(Application->ExeName);
      iniFile      = ChangeFileExt( ExtractFileName(Application->ExeName), ".ini" );
    
      // Sound-File-Ordner
      SoundFolder = ExtractFilePath(Application->ExeName) + "Sounds\\";
    
      // Programm-Version und letztes Update
      strVersion     = "Vorlage v0.1a";
      strLastUpdate  = "16.08.2013 - 15:40 Uhr";
    
      // Startzeitpunkt der Anwendung
      iStartTime = ::GetTickCount();
    
      // Sound-Auswahl für den MediaPlayer - "Intro"
      Sound = "Intro";
    
      // ini-Datei laden
      ini_Load();
    }
    //---------------------------------------------------------------------------
    
    // ##########################################################################      ###  Destruktor
    // ### Destruktor - Speicher aller initialisierten Variablen freigeben    ###
    // ##########################################################################
    
    //---------------------------------------------------------------------------
    __fastcall TMain::~TMain()
    {
      // ini-Datei speichern
      ini_Save();
    }
    //---------------------------------------------------------------------------
    
    // ##########################################################################      ### ini-Datei Laden / Speichern
    // ### ini-Datei Laden / Speichern                                        ###
    // ##########################################################################
    
    //---------------------------------------------------------------------------
    void __fastcall TMain::ini_Load()
    {
      TStringList *ini = new TStringList();
    
      try
      {
        ini->LoadFromFile(Programmpfad + iniFile);
    
        //-----
    
        // Position
        if (ini->Values["posX"] != "" && ini->Values["posY"] != "")
        {
          Main->Left = StrToInt(ini->Values["posX"]);
          Main->Top  = StrToInt(ini->Values["posY"]);
        }
        else
        {
          // Formular in Bildschirmmitte rücken
          Main->Left = int(Screen->Width  / 2) - int(Main->ClientWidth  / 2);
          Main->Top =  int(Screen->Height / 2) - int(Main->ClientHeight / 2);
        }
      }
      catch(...)
      {
        // Formular in Bildschirmmitte rücken
        Main->Left = int(Screen->Width  / 2) - int(Main->ClientWidth  / 2);
        Main->Top =  int(Screen->Height / 2) - int(Main->ClientHeight / 2);
      }
    
      // Speicher freigeben
      delete ini;
    }
    //---------------------------------------------------------------------------
    void __fastcall TMain::ini_Save()
    {
      TStringList *ini = new TStringList();
    
      // Position
      ini->Add("[Position]");
      ini->Add("posX=" + String(Main->Left));
      ini->Add("posY=" + String(Main->Top));
    
      // ini File speichern
      try
      {
        ini->SaveToFile(Programmpfad + iniFile);
      }
      catch (Exception *E)
      {
        String msg  = "Fehler beim Speichern der ini-Datei: \n";
               msg += "\" " + E->Message + " \"";
        Application->MessageBox( msg.c_str(), strVersion.c_str(), 0+64 );
      }
    
      // Speicher freigeben
      delete ini;
    }
    //---------------------------------------------------------------------------
    
    // 'UnitLaufzeit.cpp' - class TMain - TTimer (Laufzeit)
    //---------------------------------------------------------------------------
    #include "UnitMain.h"
    //---------------------------------------------------------------------------
    
    // ##########################################################################      ### Timer - Laufzeit der Anwendung
    // ### Timer - Laufzeit der Anwendung                                     ###
    // ##########################################################################
    
    //---------------------------------------------------------------------------
    void __fastcall TMain::LaufzeitTimer(TObject *Sender)
    {
      // Laufzeit berechnen
      int iRunTime = ::GetTickCount() - iStartTime;
    
      // Variablen für die Berechnung
      int h, m, s;
    
      // ---
      // RunTime in Stunden, Minuten und Sekunden umwandeln
    
      // Stunden
      h = int( (iRunTime / 1000) / 60 / 60 );
    
      // Minuten
      m  = int( (iRunTime / 1000) / 60 ) - (h * 60);
    
      // Sekunden
      s  = int( (iRunTime / 1000) - (m * 60) );
    
      // Variablen für die Anzeige
      String H = "", M = "", S = "";
    
      // berechnete Werte zuweisen
      H = String(h);
      M = String(m);
      S = String(s);
    
      // Länge der Werte für die Anzeige anpassen - Format: "##"
      if (h < 10) H = "0" + H;
      if (m < 10) M = "0" + M;
      if (s < 10) S = "0" + S;
    
      // ---
    
      // aktuelle Laufzeit anzeigen
      AnzZeit->Caption = H + ":" + M + ":" + S;
    }
    //---------------------------------------------------------------------------
    
    // 'UnitSound.cpp' - class TMain - TTimer (Sound)
    //---------------------------------------------------------------------------
    #include "UnitMain.h"
    //---------------------------------------------------------------------------
    
    // ##########################################################################      ### Timer - Sound abspielen?
    // ### Timer - prüfen ob ein Sound abgespielt werden soll                 ###
    // ##########################################################################
    
    //---------------------------------------------------------------------------
    void __fastcall TMain::TimerSoundTimer(TObject *Sender)
    {
      if (Sound != "")
      {
        // Sound gefunden und abspielen?
        bool bPlay = true;
    
        if (Sound == "Intro")
        {
          MediaPlayer->FileName = SoundFolder + "Intro.mp3";
        }
        else
        {
          // kein Sound ausgewählt - MediaPlayer nicht starten
          bPlay = false;
        }
    
        // ---
        // Sound Reset
        Sound = "";
    
        // ---
        // Sound abspielen
        if (bPlay)
        {
          try
          {
            MediaPlayer->Open();
            MediaPlayer->Play();
          }
          catch (Exception *E)
          {
            String msg  = "Fehler beim Abspielen des Sounds: \n";
                   msg += "\" " + E->Message + " \"";
            Application->MessageBox( msg.c_str(), strVersion.c_str(), 0+64 );
          }
        }
        // end: if (bPlay)
      }
      // end: if (Sound != "")
    }
    //---------------------------------------------------------------------------
    
    // 'UnitMain.h' - class TMain - Header
    //---------------------------------------------------------------------------
    #ifndef UnitMainH
    #define UnitMainH
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    #include <ExtCtrls.hpp>
    #include <MPlayer.hpp>
    //---------------------------------------------------------------------------
    class TMain : public TForm
    {
      //-------------------------------------------------------------------------
      __published:	// Von der IDE verwaltete Komponenten
    
            // Timer und Panel zum Anzeigen der Laufzeit
            TTimer *Laufzeit;
            TPanel *AnzZeit;
    
            // Timer und MediaPlayer zum Abspielen von Sounds
            TMediaPlayer *MediaPlayer;
            TTimer *TimerSound;
    
            // 'OnTimer()'-Methode von TTimer (Laufzeit)
            void __fastcall LaufzeitTimer(TObject *Sender);
    
            // 'OnTimer()'-Methode von TTimer (Sounds)
            void __fastcall TimerSoundTimer(TObject *Sender);
    
      //-------------------------------------------------------------------------
      private:	// Anwender-Deklarationen
    
        // Programmpfad und ini-Datei
        String Programmpfad, iniFile;
    
        // ini-File laden/speichern
        void __fastcall ini_Load();
        void __fastcall ini_Save();
    
        // Startzeitpunkt der Anwendung
        int iStartTime;
    
        // ---
    
        // Sound-File-Ordner
        String SoundFolder;
    
        // Sound-Auswahl für den MediaPlayer
        String Sound;
    
      //-------------------------------------------------------------------------
      public:	// Anwender-Deklarationen
    
        // ---
        // public-Variablen - Zugriff aus allen Threads/Formularen möglich
    
        // Programm-Version und letztes Update
        String strVersion, strLastUpdate;
    
        //-----------------------------------------------------------------------
    
        // Konstruktor - deklarierte Variablen initialisieren
        __fastcall TMain(TComponent* Owner);
    
        // Destruktor - Speicher aller initialisierten Variablen freigeben
        __fastcall ~TMain();
    
        //-----------------------------------------------------------------------
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TMain *Main;
    //---------------------------------------------------------------------------
    #endif
    

    Zur Übersichtlichkeit hab ich mal angefangen die 'OnTimer()'-Methoden in eigene Units (*.cpp) zu packen.
    Falls dann später noch mehr Funktionen darüber aufgerufen werden, habe ich die direkt beisammen.

    Aber bis jetzt funktioniert das alles erstmal sehr gut. Muss mal sehen ob ich mein vorhandenes Programm
    mit den Threads jetzt so umbauen kann, dass am Ende alles über die 'TTimer' läuft ohne zu Ruckeln.



  • Ich bin gerade immernoch dabei meine "alte Version" mit den Threads komplett auf 'TTimer' umzustellen.

    Gerade eben konnte ich nun mal den ersten kleinen Test laufen lassen, bei dem alle 'TTimer' parallel
    laufen und Sound abgespielt wird, die Lebenspunkte werden berechnet und angezeigt und eine Aufgabe
    gelöst werden muss.

    Hat alles super geklappt und läuft (unerwartet) flüssig.
    Vielen Dank für den Tipp mit dem TTimer!

    Ich werde bestimmt nochmal 2 bis 3 Stunden brauchen, bis ich das Programm komplett auf TTimer
    umgestellt habe, aber der Aufwand lohnt sich hier ja auf jeden Fall. Vorallem habe ich dann nicht
    mehr das Problem mit dem 'Synchronize()' und/oder den Zugriffsrechten. 😋



  • Bitte sehr 🙂 und noch viel Erfolg bei deinem Projekt.



  • Th69 schrieb:

    Bitte sehr 🙂 und noch viel Erfolg bei deinem Projekt.

    Danke! Es läuft sehr gut bis jetzt.

    Die Umstellung auf 'TTimer' hat wirklich ein paar große Verbessungen gebracht.
    Ich muss mich nicht mehr mit Zugriffsverletzungen rumschlagen und das Programm läuft jetzt
    auch viel schneller. Wenn ich also Sachen dynamisch erstelle und diese dann später wieder
    lösche, dann geht das jetzt verdammt schnell und ohne Verzögerung. 😉

    Ich habe das Projekt jetzt komplett von den Threads befreit und arbeite jetzt nur noch
    mit dem 'TTimer' und bin auch schon ein Stück weiter als vor der Umstellung. Bei dieser
    Gelegenheit habe ich auch gleich mal ein bisschen aufgeräumt und durch die Umstellung
    muss ich nun auch keine Werte mehr an die Threads übergeben, was schneller und besser ist.

    Gut das ich hier mal meinen Quellcode gepostet habe. Nochmal vielen lieben Dank!
    Ich hoffe ich bekomme das Projekt in den nächsten 14 Tagen fertig. 😃


Anmelden zum Antworten