Autor Beitrag
Klabautermann
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Veteran
Beiträge: 6366
Erhaltene Danke: 60

Windows 7, Ubuntu
Delphi 7 Prof.
BeitragVerfasst: Di 15.07.03 17:32 
Hallo,

ich bin momentan dabei mein wissen über Treads zu erweitern.

Die Frage heute, darf ich Thread-Objekte public funktionen verpassen und die dann aufrufen, und laufen diese dann auch in dem Thread zu dem sie gehören oder laufen die im Aufrufenden Thread?

Ein Beispiel. Ich habe ein Projekt mit zwei Units, beide kennen sich. Unit1 Enhällt ein Formular mit 1 Label und 1 knopf. Unit2 enthällt ein Thread Objekt. Der Thread hat eine nicht öffendliche Integer Variable i welche mit 0 (null) initialisiert wird. Deswelteren gibt es eine eine public procedure Inc, welche den Wert i um 1 erhöht und das ergebnis auf den Label ausgibt.

Inc wird immer dann aufgerufen wenn im Formular von Unit1 auf den Knopf gedrückt wird.

Im Quelltext sieht das so aus:
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:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    Thread : tTestThread;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Thread := tTestThread.create(FALSE);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Thread.Inc;
end;

end.

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:
unit Unit2;

interface

uses
  Classes;

type
  tTestThread = class(TThread)
  private
    i : Integer;
  protected
    Procedure ShowValue;
    procedure Execute; override;
  public
    procedure Inc;
    constructor create(CreateSuspended: Boolean);
  end;

implementation

  USES Unit1, Windows, SysUtils;

{ tTestThread }

constructor tTestThread.create(CreateSuspended: Boolean);
begin
  inherited;
  i := 0;
end;

procedure tTestThread.Execute; // Läuft in einer endlosschleife
  VAR
    MSG : TMsg;
begin
  WHILE (NOT Terminated) DO BEGIN
    IF (PeekMessage(Msg, 000, PM_Remove)) THEN BEGIN
      TranslateMessage(Msg);
      DispatchMessage(Msg)
    END // Message Verarbeitung
    ELSE
      Sleep(0); // CPU Zeit Freigeben, wenn keine Message vorliegt
  END// Solande der Thread nicht Terminiert wurde
end;

procedure tTestThread.Inc;
begin
  i := i + 1;
  Synchronize(ShowValue);
end;

procedure tTestThread.ShowValue;
begin
  Form1.Label1.Caption := IntToStr(i);
end;

end.


Mich interessiert nun ob man das so machen darf, oder ob es zu problemen kommt? Muss man noch etwas beachten, oder läuft das anders als ich es mir vorstrelle? Gibt es einen anderen (besseren) Weg um einen Thread, der ansonsten unabhängig läuft Befehle zu geben?

Fragen über Fragen, währe nett wenn mir der eine oder die andere ein paar Antworten geben könnte.

Gruß
Klabautermann


Zuletzt bearbeitet von Klabautermann am Di 15.07.03 22:44, insgesamt 1-mal bearbeitet
tommie-lie
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 4373

Ubuntu 7.10 "Gutsy Gibbon"

BeitragVerfasst: Di 15.07.03 19:10 
AFAIK wird nur der Inhalt von Execute in den Thread abgespalten, alles andere bleibt im aufrufenden Thread (rufst du aus Execute heraus eine Methode des TThread-Abkömmlings auf, ist diese also ebenfalls im Thread!!!).
Da du in Execute, also in dem zweiten Thread, nicht auf i zugreifst, wäre der Code so also in Ordnung. Das Synchronize(ShowValue) ist also gar nicht nötig, da das Label im Speicherbereich des gleichen Threads liegt. Würdest du beispielsweise in Execute im Sekundentakt i erhöhen und das auf das label ausgeben, bräuchtest du Synchronize, weil du aus dem Parallelthread (Execute) versuchst auf das Label aus dem Hauptthread zuzugreifen.

Eine Möglichkeit, einem Thread irgendetwas mitzuteilen, wäre PostThreadMessage. Da du ja schon eine Nachrichtenschleife hast (in Execute), wäre das die Stnadardlösung. Da du aber (noch?) kein Fenster hast, bringt dir DispatchMessage nichts, stattdessen so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure tTestThread.Execute; // Läuft in einer endlosschleife 
  VAR
    MSG : TMsg;
begin
  WHILE (NOT Terminated) DO BEGIN
    IF (PeekMessage(Msg, 000, PM_Remove)) THEN BEGIN
      TranslateMessage(Msg);

      case Msg.message of
        MyMsg_AbortThread:
          Terminated := True;
        end;

    END // Message Verarbeitung
    ELSE
      Sleep(0); // CPU Zeit Freigeben, wenn keine Message vorliegt
  END// Solande der Thread nicht Terminiert wurde
end;

Du musst also das, was normalerweise in der WndProc steht, direkt in deiner Schleife machen.
MyMsg_AbortThread sollte eine Konstante sein und auf jeden Fall größer als WM_USER sein, beuispielsweise
ausblenden Delphi-Quelltext
1:
2:
3:
4:
const
  MyMsg_AbortThread = WM_USER + 1;
  AnotherMessage = WM_USER + 2;
  // usw...



Wieder eine andere Möglichkeit ( :-) ) wäre es, Variablen zu setzen (von außen) und in einer Endlosschleife innerhalb von Execute per Polling abzufragen. Das dürfte wohl die uneffektivste Methode sein, ein Beispiel bedarf es hier wohl nicht ;-)

_________________
Your computer is designed to become slower and more unreliable over time, so you have to upgrade. But if you'd like some false hope, I can tell you how to defragment your disk. - Dilbert
Klabautermann Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Veteran
Beiträge: 6366
Erhaltene Danke: 60

Windows 7, Ubuntu
Delphi 7 Prof.
BeitragVerfasst: Di 15.07.03 21:15 
tommie-lie hat folgendes geschrieben:
Wieder eine andere Möglichkeit ( :-) ) wäre es, Variablen zu setzen (von außen) und in einer Endlosschleife innerhalb von Execute per Polling abzufragen. Das dürfte wohl die uneffektivste Methode sein, ein Beispiel bedarf es hier wohl nicht ;-)

Dadran hatte ich auch schon gedacht, aber das war mir zu "hässlich".

tommie-lie hat folgendes geschrieben:
AFAIK wird nur der Inhalt von Execute in den Thread abgespalten, alles andere bleibt im aufrufenden Thread (rufst du aus Execute heraus eine Methode des TThread-Abkömmlings auf, ist diese also ebenfalls im Thread!!!).


Mir war als hätte ich auch mal soetwas gelesen, deshalb fragte ich so misstrauisch.

tommie-lie hat folgendes geschrieben:
Eine Möglichkeit, einem Thread irgendetwas mitzuteilen, wäre PostThreadMessage. Da du ja schon eine Nachrichtenschleife hast (in Execute), wäre das die Stnadardlösung. Da du aber (noch?) kein Fenster hast, bringt dir DispatchMessage nichts


Ich hoffe doch das darf ich Kombinieren ;).

Ich habe mal mein Beispiel so umgearbeitet:
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:
unit Unit2; // Die Thread Unit
[...]
const
  MyMsg_AbortThread = WM_USER + 1;
  MyMsg_Inc = WM_USER + 2;
[...]
procedure tTestThread.Execute;
  VAR
    MSG : TMsg;
begin
  WHILE (NOT Terminated) DO BEGIN
    IF (PeekMessage(Msg, 000, PM_Remove)) THEN BEGIN
      TranslateMessage(Msg);
      case Msg.message of
        MyMsg_AbortThread:
          Self.Terminate;
        MyMsg_Inc:
          Self.Inc;
        ELSE
          DispatchMessage(Msg);
      end;
    END // Message Verarbeitung
    ELSE
      Sleep(0); // CPU Zeit Freigeben, wenn keine Message vorliegt
  END// Solande der Thread nicht Terminiert wurde
end;

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
unit Unit1; // Das Form
[...]
procedure TForm1.Button1Click(Sender: TObject);
begin
   PostThreadMessage(Thread.ThreadID, MyMsg_Inc, 00);
end;


Das funktioniert auch ganz wunderbar. Wenn ich dich richtig verstanden habe, dann jetzt auch in der Form wie ich es mir wünsche, nähmlich so, dass die wahnsinnig komplizierte Berechnung im Thread2 gemacht wird und erst wenn sie Fertig ist, die Ausgabe in das Label des Haupt Threads gemacht wird.

Wenn das so stimmt dann danke ich dir viel mals, und werde mcih jetzt mit den zufriedenen Gefühl wieder etwas gelernt zu haben über mein Abendessen her machen ;).

Gruß
Klabautermann
tommie-lie
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 4373

Ubuntu 7.10 "Gutsy Gibbon"

BeitragVerfasst: Di 15.07.03 21:59 
Klabautermann hat folgendes geschrieben:
tommie-lie hat folgendes geschrieben:
Polling

Dadran hatte ich auch schon gedacht, aber das war mir zu "hässlich".

Stimmt, deswegen sprach ich auch von uneffektivst ;-)

Zitat:
Mir war als hätte ich auch mal soetwas gelesen, deshalb fragte ich so misstrauisch.

Also ich bin mir zu 95% sicher.

Zitat:
Wenn ich dich richtig verstanden habe, dann jetzt auch in der Form wie ich es mir wünsche, nähmlich so, dass die wahnsinnig komplizierte Berechnung im Thread2 gemacht wird und erst wenn sie Fertig ist, die Ausgabe in das Label des Haupt Threads gemacht wird.

Ja, die wahnsinnig komplizierte Berechung findet nun im zweiten Thread statt. Das kannst du auch ausprobieren, indem du die Rechnung noch viel wahnsinnig schwerer machst (z.B. durch sleep *g*) und im Hauptthread und im zweiten Thread ein paar ShowMessages an diverse Stellen einfügst.

Allerdings wird jetzt das Synchronize zwingend notwendig!


Zitat:
Wenn das so stimmt dann danke ich dir viel mals, und werde mcih jetzt mit den zufriedenen Gefühl wieder etwas gelernt zu haben über mein Abendessen her machen ;).

Aber keine Krümel auf die Tastatur! :mrgreen:

_________________
Your computer is designed to become slower and more unreliable over time, so you have to upgrade. But if you'd like some false hope, I can tell you how to defragment your disk. - Dilbert
Klabautermann Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Veteran
Beiträge: 6366
Erhaltene Danke: 60

Windows 7, Ubuntu
Delphi 7 Prof.
BeitragVerfasst: Di 15.07.03 22:44 
Hi,
tommie-lie hat folgendes geschrieben:
Ja, die wahnsinnig komplizierte Berechung findet nun im zweiten Thread statt.

hervorragend, dann kann ich die Tage ja mal was realisieren, was mehr sinn macht als der Beispiel Thread :mrgreen:.

tommie-lie hat folgendes geschrieben:
Allerdings wird jetzt das Synchronize zwingend notwendig!

Damit und mit der Tatsache, dass ich mir ein paar Messages definieren muss kann ich problemlos leben.

tommie-lie hat folgendes geschrieben:
Aber keine Krümel auf die Tastatur! :mrgreen:

Nicht doch, ich bin dafür extra in einen anderen Raum gegangen (sonst hätte ich zum Essen ja auch keine Hände frei :roll:).

Gruß
Klabautermann
tommie-lie
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 4373

Ubuntu 7.10 "Gutsy Gibbon"

BeitragVerfasst: Di 15.07.03 23:35 
Titel: r nciht geschrieben.
Klabautermann hat folgendes geschrieben:
tommie-lie hat folgendes geschrieben:
Allerdings wird jetzt das Synchronize zwingend notwendig!

Damit und mit der Tatsache, dass ich mir ein paar Messages definieren muss kann ich problemlos leben.

Du kannst auch selbst eine CriticalSection benutzen. Dafür gibt's auch ein VCL-Objekt.
Aber Synchronize wird das schon selber machen, wäre nur ein Hinweis für Wissbegierige :-P

_________________
Your computer is designed to become slower and more unreliable over time, so you have to upgrade. But if you'd like some false hope, I can tell you how to defragment your disk. - Dilbert
Klabautermann Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Veteran
Beiträge: 6366
Erhaltene Danke: 60

Windows 7, Ubuntu
Delphi 7 Prof.
BeitragVerfasst: Mi 16.07.03 00:03 
Titel: Re: r nciht geschrieben.
Hi,
tommie-lie hat folgendes geschrieben:
Du kannst auch selbst eine CriticalSection benutzen. Dafür gibt's auch ein VCL-Objekt.
Aber Synchronize wird das schon selber machen, wäre nur ein Hinweis für Wissbegierige :-P

noch so 'ne Sache die ich momentan nicht zuordnen kann user defined image, ich habe mal im Zug was drüber gelesen und meine das es auch nicht so schwierig war, da fehlte aber die Praxis zum verfesstigen.
Also kommt Praxis kommt wissen.
Auf jeden Fall hast du mich jetzt soweit auf den Damm gebracht, dass ich meine nächsten Ideen verwirklichen kann. Wenn Probleme auftauchen oder ich auf neue Ideen komme weiß ich ja wo ich hilfe finde ;).

Gruß
Klabautermann
tommie-lie
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 4373

Ubuntu 7.10 "Gutsy Gibbon"

BeitragVerfasst: Mi 16.07.03 14:11 
Titel: Re: r nciht geschrieben.
Klabautermann hat folgendes geschrieben:
tommie-lie hat folgendes geschrieben:
CriticalSection

noch so 'ne Sache die ich momentan nicht zuordnen kann user defined image, ich habe mal im Zug was drüber gelesen und meine das es auch nicht so schwierig war, da fehlte aber die Praxis zum verfesstigen.

Stimmt, schwierig ist es nicht. Die Klasse heißt glaub' ich TCriticalSection. Beim start des programmes sollte man sie createn, beim beenden wieder freigeben. Wie dann die Befehle zum eintreten und verlassen genau heißen, weiß ich nciht mehr, sieht man aber in der CodeCompletion ;-)
Auf jeden Fall musst du in eine CS "eintreten", dann werden alle anderen Threads des gleichen Prozesses auf Eis gelegt und du kannst beliebig Variablen schreiben, ohne daß Gefahr besteht, daß ein anderer Thread gleichzeitig drauf zugreift. Beim verlassen der CS werden die anderen Threads dann wieder gestartet.
Für Zahlen und Pointer bieten sich übrigens die API-Funktionen InterLockedXXX an, einfach mal im SDK schauen. Die tauschen Variablen aus und schützen den Speicher autoamtisch, praktisch wenn man überlegt, ob man wegen einem einzigen Integer extra 'ne CS benutzen muss ;-)

Zitat:
Wenn Probleme auftauchen oder ich auf neue Ideen komme weiß ich ja wo ich hilfe finde ;).

Jupp :-)

_________________
Your computer is designed to become slower and more unreliable over time, so you have to upgrade. But if you'd like some false hope, I can tell you how to defragment your disk. - Dilbert
Brueggendiek
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 304

Win 98, Win98SE, Win XP Home
D5 Std
BeitragVerfasst: Mi 16.07.03 21:53 
Hallo!

tommie-lie hat folgendes geschrieben:
Auf jeden Fall musst du in eine CS "eintreten", dann werden alle anderen Threads des gleichen Prozesses auf Eis gelegt und du kannst beliebig Variablen schreiben, ohne daß Gefahr besteht, daß ein anderer Thread gleichzeitig drauf zugreift. Beim verlassen der CS werden die anderen Threads dann wieder gestartet.


Das ist so nicht richtig!

Bei einer Critical Section kann nur einer eintreten. Versucht ein anderer Thread, auch einzutreten, muß er warten, bis frei ist. Wer nicht eintreten will, kann weitermachen.
Es muß also in allen Fällen, wo gemeinsame Daten geändert werden, am Anfang "cs.Enter" und am Ende "cs.Leave" stehen. Daß wir das Ganze mit einer kleinen Prise "TRY- FINALLY" garnieren, sollte selbstverständlich sein.

Gruß

Dietmar Brüggendiek
tommie-lie
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 4373

Ubuntu 7.10 "Gutsy Gibbon"

BeitragVerfasst: Mi 16.07.03 23:33 
Brueggendiek hat folgendes geschrieben:
Das ist so nicht richtig!

Bei einer Critical Section kann nur einer eintreten. Versucht ein anderer Thread, auch einzutreten, muß er warten, bis frei ist. Wer nicht eintreten will, kann weitermachen.
Es muß also in allen Fällen, wo gemeinsame Daten geändert werden, am Anfang "cs.Enter" und am Ende "cs.Leave" stehen. Daß wir das Ganze mit einer kleinen Prise "TRY- FINALLY" garnieren, sollte selbstverständlich sein.

Hmm, wo zum ...
ahhh ja... hier:
Thread-Tutorial von Delphi-Source.de hat folgendes geschrieben:
Da alle anderen Threads blockiert werden, während sich ein Thread im kritischen Abschnitt befindet, wird die Anwendung langsamer. Kritische Abschnitte sollten also nur verwendet werden, wenn es erforderlich ist.

Was du gesagt hast, würde bedeuten, daß alle anderen Threads des gleichen Prozesses weiterlaufen, bis sie ebenfalls in eine CS eintreten wollen, dann warten sie, bis der andere Threead draußen ist.
Das beißt sich aber irgendwie mit der Aussage aus dem Tutorial.

Nach dem, was ich eben im SDK nachgelesen habe, scheinst du aber Recht zu haben. Die (API-)Funktion EnterCriticalSection wartet selbstständig, bis die CS vom aufrufenden Thread betreten werden darf, also bis der Thread, der im Moment drin ist, sie wieder verlassen hat. Das schließt natürlich aus, daß alle anderen Threads automatisch gestoppt werden, denn dann müsste EnterCriticalSection ja nicht mehr warten, weil es ja nix zu warten gibt.

_________________
Your computer is designed to become slower and more unreliable over time, so you have to upgrade. But if you'd like some false hope, I can tell you how to defragment your disk. - Dilbert