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.