Hallo Leute!
Ich habe ein kleines Problem mit grossen Folgen:
In meiner Applikation greife ich von drei Threads aus auf Daten in einer SPS-Steuerung zu. Das ganze funktioniert bis zu dem Zeitpunkt wo der Benutzer das Programm beenden möchte noch ganz gut. Aber beim Beenden über Terminate werden bloss zwei der drei Threads korrekt beendet. Der dritte kommt dann nicht in seinem OnTerminate-Event an. Das Programm scheint in der WaitFor-Zeile stehen zu bleiben.
Das kuriose daran ist, das dieses Phänomen in Abhängigkeit vom Zeitpunkt des Beendens (Klick des Users) zu stehen scheint.
Jetzt kommt der vereinfachte Quellcode, der vom Aufbau her für alle drei Threads gleich ist.
/////////////////////////////////////////////
type TBDErfThread = class (TThread)
private
FEvent : THandle;
Thread_Anzeige : TStatusBar;
str_AnzeigeText : String;
c_WaitTime: Cardinal;
i_proz : Integer;
procedure Work();
procedure RefreshStatus();
procedure ThreadEnd(Sender : TObject);
protected
procedure Execute; override;
public
constructor Create(Anzeige : TStatusBar);
procedure TerminateEx;
procedure SetWaitTime(c_NewWaitTime : Cardinal);
end;
constructor TBDErfThread.Create(Anzeige : TStatusBar);
begin
inherited Create(TRUE);
FreeOnTerminate := TRUE;
OnTerminate := ThreadEnd;
Thread_Anzeige := Anzeige;
SetWaitTime(1000 * 5);
Resume();
end;
procedure TBDErfThread.TerminateEx;
begin
Thread_Anzeige.Panels[2].Text := 'Terminate BDE-Thread...';
Synchronize(Thread_Anzeige.Refresh);
Terminate;
end;
procedure TBDErfThread.Execute;
begin
FEvent := CreateEvent(nil, False, False, nil);
try
// Loggen ('Execute...1');
while not Terminated do
begin
if WaitForSingleObject(FEvent, c_WaitTime) = WAIT_OBJECT_0 then break;
// Loggen('Execute...2');
Synchronize(Work);
end;
finally
// Loggen('Execute...3');
CloseHandle(FEvent);
end;
end;
procedure TBDErfThread.Work();
begin
// Auf Benutzer reagieren
Application.ProcessMessages;
// Daten aus SPS holen, verarbeiten und in Datenbank ablegen
end;
procedure TBDErfThread.SetWaitTime(c_NewWaitTime : Cardinal);
begin
c_WaitTime := c_NewWaitTime;
end;
procedure TBDErfThread.ThreadEnd(Sender: TObject);
var
str_Msg : String;
begin
//Loggen('TBDErfThread.ThreadEnd wurde ausgeführt...');
arr_Threads.Delete(arr_Threads.IndexOf(BDErfThread ));
end;
////// Im Hauptprogramm
var
arr_Threads : TList; // global definiert
// Beim Start des Programms
begin
arr_Threads := TList.Create();
// Erstellen und starten des Thread 1
if STErfThread = nil then
begin
// erstellen und starten
STErfThread:= TSTErfThread.Create(frm_Hauptmenue.stb_Status1);
arr_Threads.Add(STErfThread);
end;
// Erstellen und starten des Thread 2
if BDErfThread = nil then
begin
// erBDEllen und starten
BDErfThread := TBDErfThread.Create(frm_Hauptmenue.stb_Status2);
arr_Threads.Add(BDErfThread);
end;
// Erstellen und starten des Thread 3
if VerfErfThread = nil then
begin
// erstellen und starten
VerfErfThread := TVerfErfThread.Create(frm_Hauptmenue.stb_Status3);
arr_Threads.Add(VerfErfThread);
end;
end;
// Beim Beenden des Programms
begin
// STErf-Thread 1 beenden
if STErfThread <> nil then
begin
STErfThread.TerminateEx;
end else
begin
// Nichts tun, da Thread schon nicht mehr da...
end;
// Thread 2 beenden
if BDErfThread <> nil then
begin
BDErfThread.TerminateEx();
end else
begin
// Nichts tun, da Thread schon nicht mehr da...
end;
// Thread 3 beenden
if VerfErfThread <> nil then
begin
VerfErfThread.TerminateEx;
end else
begin
// Nichts tun, da Thread schon nicht mehr da...
end;
while arr_Threads.Count <> 0 do
begin
//
Item := arr_Threads.First;
if Item <> nil then
begin
Item.WaitFor;
Item := nil; // nur zur Sicherheit
end;
end;
// weiter aufräumen
end;
/////////////////////////////////////////////
Ich bin für jeden Hinweis dankbar!
Ich habe ein kleines Problem mit grossen Folgen:
In meiner Applikation greife ich von drei Threads aus auf Daten in einer SPS-Steuerung zu. Das ganze funktioniert bis zu dem Zeitpunkt wo der Benutzer das Programm beenden möchte noch ganz gut. Aber beim Beenden über Terminate werden bloss zwei der drei Threads korrekt beendet. Der dritte kommt dann nicht in seinem OnTerminate-Event an. Das Programm scheint in der WaitFor-Zeile stehen zu bleiben.
Das kuriose daran ist, das dieses Phänomen in Abhängigkeit vom Zeitpunkt des Beendens (Klick des Users) zu stehen scheint.
Jetzt kommt der vereinfachte Quellcode, der vom Aufbau her für alle drei Threads gleich ist.
/////////////////////////////////////////////
type TBDErfThread = class (TThread)
private
FEvent : THandle;
Thread_Anzeige : TStatusBar;
str_AnzeigeText : String;
c_WaitTime: Cardinal;
i_proz : Integer;
procedure Work();
procedure RefreshStatus();
procedure ThreadEnd(Sender : TObject);
protected
procedure Execute; override;
public
constructor Create(Anzeige : TStatusBar);
procedure TerminateEx;
procedure SetWaitTime(c_NewWaitTime : Cardinal);
end;
constructor TBDErfThread.Create(Anzeige : TStatusBar);
begin
inherited Create(TRUE);
FreeOnTerminate := TRUE;
OnTerminate := ThreadEnd;
Thread_Anzeige := Anzeige;
SetWaitTime(1000 * 5);
Resume();
end;
procedure TBDErfThread.TerminateEx;
begin
Thread_Anzeige.Panels[2].Text := 'Terminate BDE-Thread...';
Synchronize(Thread_Anzeige.Refresh);
Terminate;
end;
procedure TBDErfThread.Execute;
begin
FEvent := CreateEvent(nil, False, False, nil);
try
// Loggen ('Execute...1');
while not Terminated do
begin
if WaitForSingleObject(FEvent, c_WaitTime) = WAIT_OBJECT_0 then break;
// Loggen('Execute...2');
Synchronize(Work);
end;
finally
// Loggen('Execute...3');
CloseHandle(FEvent);
end;
end;
procedure TBDErfThread.Work();
begin
// Auf Benutzer reagieren
Application.ProcessMessages;
// Daten aus SPS holen, verarbeiten und in Datenbank ablegen
end;
procedure TBDErfThread.SetWaitTime(c_NewWaitTime : Cardinal);
begin
c_WaitTime := c_NewWaitTime;
end;
procedure TBDErfThread.ThreadEnd(Sender: TObject);
var
str_Msg : String;
begin
//Loggen('TBDErfThread.ThreadEnd wurde ausgeführt...');
arr_Threads.Delete(arr_Threads.IndexOf(BDErfThread ));
end;
////// Im Hauptprogramm
var
arr_Threads : TList; // global definiert
// Beim Start des Programms
begin
arr_Threads := TList.Create();
// Erstellen und starten des Thread 1
if STErfThread = nil then
begin
// erstellen und starten
STErfThread:= TSTErfThread.Create(frm_Hauptmenue.stb_Status1);
arr_Threads.Add(STErfThread);
end;
// Erstellen und starten des Thread 2
if BDErfThread = nil then
begin
// erBDEllen und starten
BDErfThread := TBDErfThread.Create(frm_Hauptmenue.stb_Status2);
arr_Threads.Add(BDErfThread);
end;
// Erstellen und starten des Thread 3
if VerfErfThread = nil then
begin
// erstellen und starten
VerfErfThread := TVerfErfThread.Create(frm_Hauptmenue.stb_Status3);
arr_Threads.Add(VerfErfThread);
end;
end;
// Beim Beenden des Programms
begin
// STErf-Thread 1 beenden
if STErfThread <> nil then
begin
STErfThread.TerminateEx;
end else
begin
// Nichts tun, da Thread schon nicht mehr da...
end;
// Thread 2 beenden
if BDErfThread <> nil then
begin
BDErfThread.TerminateEx();
end else
begin
// Nichts tun, da Thread schon nicht mehr da...
end;
// Thread 3 beenden
if VerfErfThread <> nil then
begin
VerfErfThread.TerminateEx;
end else
begin
// Nichts tun, da Thread schon nicht mehr da...
end;
while arr_Threads.Count <> 0 do
begin
//
Item := arr_Threads.First;
if Item <> nil then
begin
Item.WaitFor;
Item := nil; // nur zur Sicherheit
end;
end;
// weiter aufräumen
end;
/////////////////////////////////////////////
Ich bin für jeden Hinweis dankbar!