Autor Beitrag
fuba
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 125

Win7
D7 Ent.
BeitragVerfasst: Fr 30.07.10 00:20 
Hi @ all

Meine frage im Detail:

Wie kann ich meine Threads "durchgehen" und prüfen ob diese noch laufen, ohne für jeden Thread eine Variable zu deklarieren?
Wie das ganze mit einem Thread funktioniert is mir klar, aber bei mehreren Threads...

code Hauptform:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

uses Unit3;

var
  Thread: TTestThread;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
begin
  ListBox1.Clear;
  for i:=0 to 3 do // als test "4" Einträge der Listbox hinzufügen
  begin
    ListBox1.Items.Append('Thread'+IntToStr(i+1));
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  aName: string;
begin
  if ListBox1.ItemIndex > -1 then
  begin
    aName:=ListBox1.Items[ListBox1.ItemIndex];

    Thread := TTestThread.Create(true);
    with Thread do
    begin
      FreeOnTerminate := True;
      Thread.ThreadName:=aName; // aName zur identifikation an den Thread übergeben
      Resume;
    end;
  end;
end;

end.


code Thread Unit:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
unit Unit3;

interface

uses
  Classes, Windows;

type
  TTestThread = class(TThread)
  ThreadName: string;
  private
    { Private-Deklarationen }
  protected
    procedure Execute; override;
  end;

implementation

procedure TTestThread.Execute;
begin
  while (not Terminated) do
  begin
    sleep(1000);
    // mach irgendwas
  end;
end;

end.


Jedes mal, wenn ich auf Button1 drücke, startet ein neuer Thread, was auch so gewollt ist.
Der Name bzw der Eintrag aus der ListBox wird als aName dem Thread übergeben, sozusagen als Identifizierung.

Aber nun möchte ich per klick auf auf die ListBox prüfen ob Thread1 noch läuft, wenn ja mit klick auf Button1 diesen wieder beenden.
Gibt es eine Möglichkeit alle meine Threads "durchzugehen" und auf aName zu prüfen?

vielen dank für eure hilfe(n)!
elundril
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: Fr 30.07.10 01:26 
Kannst du die referenz nicht in einem array of TTestThread speichern? Hab aber noch nie mit Threads programmiert (in delphi zumindest), insofern is es nur ein vorschlag.

lg elundril

_________________
This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
fuba Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 125

Win7
D7 Ent.
BeitragVerfasst: Fr 30.07.10 02:51 
user profile iconelundril hat folgendes geschrieben Zum zitierten Posting springen:
Kannst du die referenz nicht in einem array of TTestThread speichern? Hab aber noch nie mit Threads programmiert (in delphi zumindest), insofern is es nur ein vorschlag.

lg elundril

hmm, daran hab ich nicht gedacht und es scheint zu klappen! :)

hier nochmal mein testcode, wie ich es jetzt gemacht habe:

Hauptunit:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    ListBox2: TListBox;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

var
  ThreadArray: array of TTestThread;
  ThreadRunning: array of Boolean;

{$R *.dfm}


procedure StartThread(ThreadName: string; ThreadIndex: integer);
begin
  ThreadArray[ThreadIndex]:=TTestThread.Create(True);
  ThreadArray[ThreadIndex].FreeOnTerminate:=True;
  ThreadArray[ThreadIndex].ThreadName:=ThreadName;
  ThreadArray[ThreadIndex].Resume;
  ThreadRunning[ThreadIndex]:=True;
end;

procedure StopThread(ThreadIndex: integer);
begin
  ThreadArray[ThreadIndex].Terminate;
  ThreadRunning[ThreadIndex]:=False;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
begin
  ListBox1.Clear;
  for i:=0 to 3 do
  begin
    ListBox1.Items.Append('Thread'+IntToStr(i+1));
  end;

  if Form1.ListBox1.Items.Count <> Form1.ListBox2.Items.Count then
  begin
    Form1.ListBox2.Clear;
    for i:=0 to Form1.ListBox1.Items.Count-1 do
    begin
      Form1.ListBox2.Items.Append('stopped');  
    end;
  end;

  SetLength(ThreadArray, ListBox1.Items.Count);
  SetLength(ThreadRunning, ListBox1.Items.Count);
  for i:=low(ThreadRunning) to high(ThreadRunning) do
  begin
    ThreadRunning[i]:=False;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  aName: string;
begin
  if ListBox1.ItemIndex > -1 then
  begin
    aName:=ListBox1.Items[ListBox1.ItemIndex];

    if ThreadRunning[ListBox1.ItemIndex] then
    begin
      StopThread(ListBox1.ItemIndex);
      Form1.Button1.Caption:='Start';
    end else
    begin
      StartThread(aName, ListBox1.ItemIndex);
      Form1.Button1.Caption:='Stop';
    end;
  end;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
var
  ThreadName: string;
begin
  ThreadName:=ListBox1.Items[ListBox1.ItemIndex];

  if ListBox1.ItemIndex > -1 then
  begin
    if ThreadRunning[ListBox1.ItemIndex] then
    begin
      Button1.Caption:='Stop';
    end else
    begin
      ListBox2.Items[ListBox1.ItemIndex]:='stopped';
      Button1.Caption:='Start';
    end;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  close;
end;

end.


Threadunit:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
unit Unit2;

interface

uses
  Classes, Windows, SysUtils;

type
  TTestThread = class(TThread)
  ThreadName: string;
  private
    { Private-Deklarationen }
    count: integer;
  public
  protected
    procedure Execute; override;
    procedure Sync;
  end;

implementation

uses Unit1;

function SecToDMS(Sekunden: int64): string;
var
  RestSekunden,
  AllesInMinuten, RestMinuten,
  AllesInStunden,RestStunden,
  Tage, RestTage, Wochen: Int64;
begin
  Result:='00:00:00';

  try
    AllesInMinuten := Sekunden div 60//Alle Minuten ausrechnen
    RestSekunden := Sekunden - (AllesInMinuten * 60);  //Restliche Sekunden berechen
    AllesInStunden := AllesInMinuten div 60;   //Alle Stunden rausholen
    RestMinuten := AllesInMinuten - (AllesInStunden * 60);   //Restliche Minuten berechnen
    Tage:= AllesInStunden div 24;
    RestStunden:= AllesInStunden - (Tage * 24);
    Wochen := Tage div 7;
    RestTage := Tage - Wochen * 7;

    if Sekunden >= 604800 then
    begin
      result :=
      formatfloat('00',Wochen)+'W, '+
      formatfloat('00',RestTage)+'T, '+
      formatfloat('00',RestStunden)+':'+
      formatfloat('00',RestMinuten)+':'+
      formatfloat('00',RestSekunden);
    end;


    if Sekunden  < 604800 then
    begin
      result :=
      formatfloat('00',RestTage)+'T, '+
      formatfloat('00',RestStunden)+':'+
      formatfloat('00',RestMinuten)+':'+
      formatfloat('00',RestSekunden);
    end;

    if Sekunden < 86400 then
    begin
      result :=
      formatfloat('00',RestStunden)+':'+
      formatfloat('00',RestMinuten)+':'+
      formatfloat('00',RestSekunden);
    end;

  except
    result := '00:00:00';
  end;
end;

procedure TTestThread.Sync;
var
  i:integer;
begin
  for i:=0 to Form1.ListBox1.Items.Count-1 do
  begin
    if Form1.ListBox1.Items[i] = ThreadName then
    begin
      if (not Terminated) then
      Form1.ListBox2.Items[i] := SecToDMS(count) else
      Form1.ListBox2.Items[i] := 'stopped';
    end;
  end;
end;

procedure TTestThread.Execute;
begin
  count:=0;
  while not Terminated do
  begin
    inc(count);
    Synchronize(Sync);
    sleep(1000);
  end;
  Synchronize(Sync);
end;

end.


also bei jedem thread den ich erstelle zählt er mir jetzt den count so wie ich es haben wollte :)
falls jemand meint es gäbe eine andere/elegantere lösung, ich bin ganz ohr :D

auf jeden fall mal ein fettes Dankeschön an elundril für die idee mit dem array :)

// Edit //
Leider hat das ganze mit "IsRunning" aus dem Thread nicht so funktioniert wie ich es haben wollte.
Deswegen ein Update -> noch ein Array of Boolean hinzugefügt, da mir jetzt nichts anderes eingefallen ist.
Gibt es eine andere möglichkeit zu prüfen ob ThreadX, welcher zu aName passt noch läuft?


Zuletzt bearbeitet von fuba am Fr 30.07.10 04:59, insgesamt 3-mal bearbeitet
Gerd Kayser
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 632
Erhaltene Danke: 121

Win 7 32-bit
Delphi 2006/XE
BeitragVerfasst: Fr 30.07.10 03:31 
Folgendes Beispiel listet alle Threads (ohne den MainThread) auf, die vom eigenen Programm gestartet wurden und noch am laufen sind:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
// uses TlHelp32;
procedure TForm1.Button2Click(Sender: TObject);
var 
  PE_Exe    : TThreadEntry32;
  HProcList : THandle;
  HExe      : THandle;
  HThread   : THandle;
begin
  Memo1.Clear;
  HExe := GetCurrentProcessID;
  HThread := GetCurrentThreadID;
  PE_Exe.dwSize := SizeOf(TThreadEntry32);

  HProcList := CreateToolHelp32SnapShot(TH32CS_SnapThread, 0);

  Thread32First(HProcList, PE_Exe);
  repeat
    if ((HExe = PE_Exe.th32OwnerProcessID) and (HThread <> PE_Exe.th32ThreadID)) then
      Memo1.Lines.Add(IntToStr(PE_Exe.th32ThreadID));
  until 
    not Thread32Next(HProcList, PE_Exe);

  CloseHandle(HProcList);
end;

Beenden kann man die Threads mit TerminateThread.
fuba Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 125

Win7
D7 Ent.
BeitragVerfasst: Fr 30.07.10 04:48 
Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

Schon klar, aber woher weiß ich welcher Thread (da ich ja nur die ThreadID bekomme) zu welchem Index in der ListBox gehört?
Mit der ThreadID kann ich zwar den jeweiligen Thread terminieren, aber nicht prüfen ob es eigentlich der richtige war (ThreadName prüfen).

Ich stelle mir das so vor (grob):
Klick auf Listbox-Eintrag-Thread1 -> Klick auf Start -> Thread1 Läuft
Klick auf Listbox-Eintrag-Thread2 -> Klick auf Start -> Thread2 Läuft
usw...

Das gleiche beim beenden:
Klick auf Listbox-Eintrag-Thread1 -> Klick auf Stop -> Thread1 wird beendet
Klick auf Listbox-Eintrag-Thread2 -> Klick auf Stop -> Thread2 wird beendet
usw...
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 30.07.10 06:21 
user profile iconGerd Kayser hat folgendes geschrieben Zum zitierten Posting springen:
Beenden kann man die Threads mit TerminateThread.

Was man aber tunlichst nicht machen sollte. Oder hältst du dein Auto an, in dem du es immer vor die Wand setzt?

Lege die ThreadIDs doch als Objekte mit in der Listbox ab. Oder besser, verwalte die Threads in einer ObjectList.
Gerd Kayser
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 632
Erhaltene Danke: 121

Win 7 32-bit
Delphi 2006/XE
BeitragVerfasst: Fr 30.07.10 07:45 
user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:
Was man aber tunlichst nicht machen sollte.

Das ist so aber nicht richtig. Man kann Threads verwenden, um a) etwas abzuarbeiten, ohne den Hauptthread lahm zu legen, oder b) um zu warten, ohne den Hauptthread zu blockieren.

Beispiel für einen Warte-Thread:
Man möchte feststellen, ob gewisse Laufwerkspfade im Netzwerk bereit sind. Bei mehreren Abfragen können sich die Zeiten bei nicht bereiten Laufwerken zu einer kleinen Ewigkeit summieren. (Die Warterei kann man gut im Windowsxplorer nachvollziehen, wenn man auf ein nicht bereites Laufwerk klickt.) Wenn man die Abfragen in Threads verlagert, kann man diese ggf. z. B. nach einer Sekunde abbrechen (also den Timeout drastisch verkürzen).
Beispiel:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var
  Form1: TForm1;
  LWZustand : string;
  Thread_laeuft: boolean;
[ ... ]

procedure TForm1.Button1Click(Sender: TObject);
var
  LWInfo : ThInfo;
begin
  LWZustand := 'Timeout';
  Thread_laeuft := True;

  LWInfo := ThInfo.Create('\\192.168.1.200\share\');
  LWInfo.Priority := tpNormal;
  Sleep(1000);
  if Thread_laeuft then
    LWInfo.Terminate;
  Label1.Caption := LWZustand;
end;

Und hier der Thread dazu:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
unit Th_Info;

interface

uses
  Windows, Classes;

type
  ThInfo = class(TThread)
  private
    { Private-Deklarationen }
  protected
    LW: string;
    procedure Execute; override;
  public
    constructor Create(Laufwerk: String);
  end;

implementation

uses Unit1;

constructor ThInfo.Create(Laufwerk: String);
begin
  inherited Create(false); // False = sofort aktiv
  FreeOnTerminate := true; // freigeben, wenn fertig
  LW := Laufwerk;
end;

procedure ThInfo.Execute;
var
  Buffer    : array [0..11of Char;
  Serien_Nr : cardinal;
  Dummy     : cardinal;
  Flags     : cardinal;
begin
  if GetVolumeInformation(PChar(LW), Buffer, SizeOf(Buffer),
                          @Serien_Nr, Dummy, Flags, nil0then
    Unit1.LWZustand := 'Ready'
  else
    Unit1.LWZustand := 'Not Ready';
  Unit1.Thread_laeuft := false;  
end;

end.
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 30.07.10 14:05 
Bitte richtig lesen. Ich bezog mich auf das Beenden eines Threads mittels TerminateThread. Denn da werden eventuell Ressourcen nicht mehr freigegeben und wenn sich der Thread gerade in einer CriticalSection befindet wird diese auch nicht mehr freigegeben und du hast ein Deadlock.

Und über die Verwendung von Threads brauchst du mich nicht aufklären: www.michael-puff.de/...i/Tutorials/Threads/
fuba Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 125

Win7
D7 Ent.
BeitragVerfasst: Fr 30.07.10 15:29 
user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:
Bitte richtig lesen. Ich bezog mich auf das Beenden eines Threads mittels TerminateThread. Denn da werden eventuell Ressourcen nicht mehr freigegeben und wenn sich der Thread gerade in einer CriticalSection befindet wird diese auch nicht mehr freigegeben und du hast ein Deadlock.

Und über die Verwendung von Threads brauchst du mich nicht aufklären: www.michael-puff.de/...i/Tutorials/Threads/

hab ich mir fast gedacht, wenn ich die threads per ThreadTerminate beende, irgendwas zurückbleibt, also unsauber ist.

aber noch ne frage:
wie mach ich das mit ner ObjectList und was währe anders zum vergleich mit dem array?
kann ich per ObjectList prüfen ob der ThreadX noch läuft?
(habe nämlich noch nie mit ObjectList gearbeitet)

Ein zusätzliches array dass mit sagt ob läuft oder nicht, ist auch nicht das ideale, finde ich.
(falls ein thread durch ne exception beendet wird, steht im array "ThreadRunning" noch immer "True".)
bummi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1248
Erhaltene Danke: 187

XP - Server 2008R2
D2 - Delphi XE
BeitragVerfasst: Fr 30.07.10 16:21 
kleines Beispiel:
ist eigenlich egal ob List, Array o.ä.
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
unit Unit5;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TMyThread=Class(TThread)
    private
    FSleepTime:Integer;
    protected
    procedure Execute; override;
  public
    constructor CreateWithInfo(CreateSuspended:Boolean;SleepTime:Integer);
  end;


  TForm5 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure MyTerminate(Sender: TObject);
    procedure RunOneThread(Dauer: Integer);
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

{ TMyThread }

constructor TMyThread.CreateWithInfo(CreateSuspended: Boolean;SleepTime:Integer);
begin

  inherited Create(CreateSuspended);
  FSleepTime := SleepTime;
  FreeOnTerminate := True;

end;

procedure TMyThread.Execute;
begin
  inherited;
  Sleep(FSleepTime);
end;


procedure TForm5.MyTerminate(Sender:TObject);
var
  i:Integer;
begin
  for I := 0 to Listbox1.Items.Count - 1 do
    begin
      if  Listbox1.Items.Objects[i]=Sender then
        begin
           Listbox1.Items[i]:= Listbox1.Items[i] + ' bendet.';
           Listbox1.Items.Objects[i]:= nil;
        end;
    end;
end;



Procedure TForm5.RunOneThread(Dauer:Integer);
begin
  Listbox1.Items.AddObject('1. Thread ' + IntToStr(DAuer) + ' Millisekunden', TMyThread.CreateWithInfo(true,dauer));
  with TMyThread(Listbox1.Items.Objects[Listbox1.Items.Count - 1]) do
      begin
        ONTerminate := MyTerminate;
        Suspended := false;
      end;

end;


procedure TForm5.Button1Click(Sender: TObject);
begin
    Randomize;
    RunOneThread(Random(5000) + 1000);
end;

end.
Gerd Kayser
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 632
Erhaltene Danke: 121

Win 7 32-bit
Delphi 2006/XE
BeitragVerfasst: Fr 30.07.10 17:45 
user profile iconfuba hat folgendes geschrieben Zum zitierten Posting springen:
hab ich mir fast gedacht, wenn ich die threads per ThreadTerminate beende, irgendwas zurückbleibt, also unsauber ist.

Ich schrieb ja auch "kann" und nicht "muß" ... Wäre ich heute morgen nicht aus dem Bett geklingelt worden und ausgeschlafen gewesen, hätte ich das sicherlich anders formuliert. :)
Zitat:
(falls ein thread durch ne exception beendet wird, steht im array "ThreadRunning" noch immer "True".)

Man kann im Thread auch auf Exceptions reagieren, z. B. so:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
    procedure Sync;
    procedure SyncAbbruch;
[ ... ]
procedure TTestThread.Execute;
begin
  count:=0;
  while not Terminated do
  begin
    inc(count);
    // Exception werfen
    if count = 15 then
      raise EMathError.Create('');
    Synchronize(Sync);
    sleep(1000);
  end;
  Synchronize(Sync);
end;
[ ... ]
procedure TTestThread.DoTerminate;
begin
  if FatalException is Exception then
    Synchronize(SyncAbbruch);
  // oder z. B. so:
  // if FatalException is Exception then
  //   LogException(Exception(FatalException));

  // Hier wird OnTerminate aufgerufen
  inherited;
end;

procedure TTestThread.SyncAbbruch;
var
  i:integer;
begin
  for i:=0 to Form1.ListBox1.Items.Count-1 do
  begin
    if Form1.ListBox1.Items[i] = ThreadName then
      Form1.ListBox2.Items[i] := 'Exception aufgetreten!';
  end;
end;

Jetzt mußt Du nur noch was einbauen, das Dein ThreadArray aktualisiert. Für mich steht jetzt erst einmal ein sehr ausgiebiger Gassirundgang mit meiner Schäferhündin an.
fuba Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 125

Win7
D7 Ent.
BeitragVerfasst: Fr 30.07.10 23:42 
user profile iconGerd Kayser hat folgendes geschrieben Zum zitierten Posting springen:

Ich schrieb ja auch "kann" und nicht "muß" ... Wäre ich heute morgen nicht aus dem Bett geklingelt worden und ausgeschlafen gewesen, hätte ich das sicherlich anders formuliert. :)

ach was, bin froh über jede idee, also np :D

// Edit //
Code aus deiesem Bereich gelöscht, da er nicht korrekt funktioniert hat.


Zuletzt bearbeitet von fuba am Sa 31.07.10 17:47, insgesamt 1-mal bearbeitet
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19340
Erhaltene Danke: 1752

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 31.07.10 10:10 
Nach Terminate kannst du übrigens mit WaitFor warten, dass er wirklich terminiert ist.
Gerd Kayser
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 632
Erhaltene Danke: 121

Win 7 32-bit
Delphi 2006/XE
BeitragVerfasst: Sa 31.07.10 14:38 
user profile iconfuba hat folgendes geschrieben Zum zitierten Posting springen:
sollte theoretisch klappen, auch wenn der Thread durch ne exception gekillt wird, oder?
Mir sind folgende Punkte aufgefallen:

1. Deine Anwendung teilt dem Anwender nicht mit, daß eine (und welche) Exception aufgetreten ist. Wenn Du das Programm mal von außerhalb der IDE startest, siehst Du, daß die Exception nicht angezeigt wird.
2. SyncTerminate führt die letzte Aktion vor dem Beenden des Threads aus. Danach folgt inherited bei DoTerminate, und der Thread verabschiedet sich. Das ist eine Sache von wenigen Millisekunden. Also macht es wenig Sinn, in SyncTerminate den Thread nochmals zu stoppen. Das ist doppelt gemoppelt.
3. "ThreadArray[ThreadIndex]:=nil;" verhindert ein nochmaliges Anstarten. Ist das so gewollt?
4. Die Button-Captions und die ListBox-Einträge werden nicht aktualisiert.
fuba Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 125

Win7
D7 Ent.
BeitragVerfasst: Sa 31.07.10 17:46 
Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

ja, ich weiß, mir ging es derzeit auch nur darum, mehrere Threads zu erstellen, zu prüfen ob ThreadX noch läuft und diesen beenden zu können.

1. Exception message wird noch eingebaut, wenn alles andere klappt wie ich es haben will
2. Das ist so gewollt, da ich es nun doch wieder mit ner array of byte mache, wo drin steht ob TreadX noch läuft.
3. hat sich erübrigt, da ich jetzt wieder mit array of byte prüfe
4. werde ich noch einbauen, danke für den fingerzeig, hätt ich fast vergessen :D

mache es jetzt übrigens so:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
...
procedure StartThread(ThreadName: string; ThreadIndex: integer);
begin
  if (Not ThreadActive[ThreadIndex]) then
  begin
    ThreadArray[ThreadIndex]:=TTestThread.Create(True);
    ThreadArray[ThreadIndex].FreeOnTerminate:=True;
    ThreadArray[ThreadIndex].ThreadName:=ThreadName;
    ThreadArray[ThreadIndex].Resume;
    ThreadActive[ThreadIndex] := True;
  end;
end;

procedure TForm1.StopThread(ThreadName: string; ThreadIndex: integer);
begin
  if ThreadActive[ThreadIndex] then
  begin
    if (ThreadArray[ThreadIndex].ThreadName = ThreadName) then
    begin
      try
        ThreadArray[ThreadIndex].Terminate;
      finally
        ThreadActive[ThreadIndex] := False;
      end;
    end;
  end;  
end;

function IsThreadActive(Index: integer): Bool;
begin
  if ThreadArray[Index] <> nil then
  begin
    Result:=ThreadActive[Index];
  end else Result:=False;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
begin
  if Form1.ListBox1.Items.Count <> Form1.ListBox2.Items.Count then
  begin
    for i:=0 to Form1.ListBox1.Items.Count-1 do
    begin
      Form1.ListBox2.Items.Append('00:00:00');
     end;
  end;

  SetLength(ThreadArray, ListBox1.Items.Count);
  SetLength(ThreadActive, ListBox1.Items.Count);
  for i:=low(ThreadActive) to high(ThreadActive) do
  begin
    ThreadActive[i] := False;
  end;
  Disable;
end;


ThreadUnit änderung:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
procedure TTestThread.SyncTerminate;
var
  i:integer;
begin
  for i:=0 to Form1.ListBox1.Items.Count-1 do
  begin
    if Form1.ListBox1.Items[i] = ThreadName then
    begin
      Form1.ListBox2.Items[i]:='00:00:00';
      Form1.ListBox3.Items[i]:='00:00:00';
      Form1.StartStopButton.Caption:='Start';
      Form1.StopThread(ThreadName, i); // weil in Form1.StopThread, ThreadActive auf False gesetzt wird, ist nur vorübergehend :D
    end;
  end;
end;

procedure TTestThread.Sync;
var
  i:integer;
begin
  for i:=0 to Form1.ListBox1.Items.Count-1 do
  begin
    if Form1.ListBox1.Items[i] = ThreadName then
    begin
      if (not Terminated) then
      begin
        Form1.ListBox2.Items[i] := SecToDMS(count);
      end else
      begin
        Form1.ListBox2.Items[i]:='00:00:00';
        Form1.StartStopButton.Caption:='Start';
      end;
    end;
  end;
end;

procedure TTestThread.Execute;
begin
  count:=0;
  while (not Terminated) do
  try
    inc(count);
    Synchronize(Sync);
    sleep(1000);
  finally
  end;
  Synchronize(Sync);
end;

procedure TTestThread.DoTerminate;
begin
  if FatalException is Exception then Synchronize(SyncTerminate);
  inherited;
end;


jetzt aber noch ne frage zu ObjectList:
könnte ich mit ner Objectlist auf anderem weg, außer nem array of bool, prüfen ob ThreadX noch läuft?
wenn ja, werde ich mir das nochmal genauer ansehen.

Danke an alle die mir bisher geholfen haben!
lg. fuba

// Edit //
die Exception kann ich ja im SyncTerminate in eine TextFile speichern oder?
habe mir das so vorgestellt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TTestThread.SyncTerminate;
var
  i:integer;
  EL: TSTringList;
begin
  for i:=0 to Form1.ListBox1.Items.Count-1 do
  begin
    if Form1.ListBox1.Items[i] = ThreadName then
    begin
      Form1.ListBox2.Items[i]:='00:00:00';
      Form1.StartStopButton.Caption:='Start';
      Form1.StopThread(ThreadName, i);
    end;
  end;

  EL := TStringList.Create;
  EL.Append(Exception(FatalException).Message);
  EL.SaveToFile(ExtractFilePath(ParamStr(0)) + 'LastError_'+ThreadName+'.txt');
  EL.Free;
end;