Autor Beitrag
whitef
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 202
Erhaltene Danke: 1

Windows X
Delphi XE X
BeitragVerfasst: Sa 25.08.12 01:32 
hi,
hab da noch eine Frage zum Thread.
Es klappt alles soweit, bis auf das Terminate aus einer anderen Unit.


Ich hab über Delphi eine neue Unit (Klasse TThread) angelegt. Ich habe den Namen "MyThread" vergeben.
Das kam in der neuen Unit raus
ausblenden Delphi-Quelltext
1:
2:
type
  MyThread  = class(TThread)


Jetzt möchte ich gerne in Unit1 auf meiner Form1 mittels button OnClick > MyThread.Terminate; ausführen um es in der Threadschleife abfragen zu können.
Deswegen habe ich in meiner neuen "Thread"-Unit namens "UnitMyThread" folgendes geändert:
ausblenden Delphi-Quelltext
1:
2:
type
  TMyThread  = class(TThread)


und das hinzugefügt ("UnitMyThread"):
ausblenden Delphi-Quelltext
1:
2:
public
  MyThread : TMyThread;


Das hier habe ich noch in "Unit1" hinzugefügt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
...
implementation

uses UnitMyThread;
...


Jetzt kann ich zwar in der "UnitMyThread" MyThread.Terminate; ausführen, aber ich möchte es ja von Unit1 aus ausführen.
Was hab ich den jetzt vergessen?

PS: Habt Nachsicht, schaut auf die Uhrzeit des Postings :-D
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 25.08.12 07:23 
Wegen deines anderen Threads:
Vorsicht, wenn du noch FreeOnTerminate nutzt. Dann solltest du keinerlei Referenz auf den Thread behalten. Wenn der Thread sich selbst verwaltet, darfst du auch nicht versuchen auf das Threadobjekt zuzugreifen. Denn du weißt ja nie wann es weg ist.

Einzige Alternative, wenn du auf das Threadobjekt zugreifen willst:
Benutze nicht FreeOnTerminate, sondern gib den Thread auch manuell von außen frei nachdem er beendet wurde (WaitFor).

Zum Problem:
Unter public kannst du nur aus dem Objekt heraus zugreife, schreibe es stattdessen in eine Klassenvariable, dann kannst du von außen zugreifen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
public
  class var
    MyThread: TMyThread;

...

TMyThread.MyThread.Terminate;
whitef Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 202
Erhaltene Danke: 1

Windows X
Delphi XE X
BeitragVerfasst: Sa 25.08.12 09:25 
Vielen Dank, nun kann ich auf Form1 TMyThread.MyThread.Terminate; aufrufen.

Aber da taucht das nächste Problem auf, ein EAccessViolation wird ausgelöst sofern man obigen Befehl ausführt:

Form1:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.Button1Click(Sender: TObject);
begin
if TMyThread.MyThread.ThreadRunning then begin
  TMyThread.MyThread.Terminate;                    ////// Hier liegt der Hund begraben... > EAccessViolation
  showmessage('terminiert');
end else
  showmessage('Zzt. kein thread läuft');
end;


UnitMyThread:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function TMyThread.ThreadRunning:boolean;
begin
  result:=running>0;
end;

procedure TMyThread.Execute;
var i : Integer;
begin
inc(running);
Synchronize(Sync_Panels);
for i := 0 to (StrToInt(varThreadDateien) - 1do
  begin
      if not Terminated then begin
        ...
      end else break;
  end;
dec(running);
end;


Button1Click:
ausblenden Quelltext
1:
...EAccessViolation...					
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Sa 25.08.12 09:32 
Also sofern das jetzt nicht bewusst als Singleton geplant ist würde ich auf die Klassenvariable verzichten.

Lege dir stattdessen ein privates Feld im Formular an. Also bei der Definition von Form1 sowas wie
ausblenden Delphi-Quelltext
1:
2:
private
MyThread: TMyThread;

Und dann beim Erstellen des Threads:
ausblenden Delphi-Quelltext
1:
MyThread := TMyThread.Create(..., ...);					

(Die Variable Thread von vorher war ja lokal, da du jetzt von woanders drauf zugreifen möchtest, musst du sie im Scope "hoch stufen")
Und dann, wie gesagt, nicht FreeOnTerminate verwenden.
whitef Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 202
Erhaltene Danke: 1

Windows X
Delphi XE X
BeitragVerfasst: Sa 25.08.12 09:42 
zum testen, ist alles in meinem aktuellen Projekt, später werde ich diese Thread Unit wieder soweit clearen, dass ich dass Grundgerüst bei anderen Projekten mitübernehmen kann.
Also ich will diesen Thread in eine extra Unit auslagern und nicht im Form1 deklarieren. Nur von Form1 aus aufrufen und beenden.
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Sa 25.08.12 10:03 
Um das deklarieren einer Variable wirst du nicht herum kommen 8)

Im Gegenteil, siehe dein eigener Code:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.btSaveClick(Sender: TObject);
var i: Integer; TargetDir, datei0, datei1, dateien: String;
Thread: TMyThread; // <<<<<<< Na was ist das denn??
begin


Und jetzt verschiebst du einfach diese Deklaration in den private-Teil deines Formulars. Dann kannst du nämlich auch aus anderen Funktionen darauf zugreifen.
Also das deklarieren ist volkommen OOP konform und widerspricht nicht der Kapselung ;)
whitef Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 202
Erhaltene Danke: 1

Windows X
Delphi XE X
BeitragVerfasst: Sa 25.08.12 10:37 
Danke!

Form1:
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:
...
uses
    ..., UnitMyThread;
...
  public
    MyThread: TMyThread;

implementation

procedure TForm1.btAbbrechenClick(Sender: TObject);
begin
MyThread := TMyThread.Create(..., ...);  
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if MyThread.ThreadRunning then begin
  MyThread.Terminate;                    ////// Er liegt immer noch dort... > EAccessViolation
  showmessage('terminiert');
end else
  showmessage('Zzt. läuft kein thread');
end;


UnitMyThread:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function TMyThread.ThreadRunning:boolean;
begin
  result:=running>0;
end;

procedure TMyThread.Execute;
var i : Integer;
begin
inc(running);
Synchronize(Sync_Panels);
for i := 0 to (StrToInt(varThreadDateien) - 1do
  begin
      if not Terminated then begin
        ...
      end else break;
  end;
dec(running);
end;
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Sa 25.08.12 10:47 
Okay, weitere Fragen:
1. Hast du das FreeOnterminate := true; entfernt?
2. Klickst du auch brav auf den Button "btAbbrechen" bevor du auf den Button "Button1" klickst?
3. Wenn's immer noch Probleme gibt, zeige bitte etwas mehr Code.
MfG