Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Trotz Threads keine Reaktion (https://www.delphipraxis.net/107165-trotz-threads-keine-reaktion.html)

Green 23. Jan 2008 14:44


Trotz Threads keine Reaktion
 
Ich habe folgendes Beispiel aus einem Buch nachgeschrieben um Threads zu verstehen, doch es funktioniert meiner Meinung nach nicht richtig.

Wenn ich es starte dann zeichnet er zwar wie blöd drauflos, was ja gewollt ist, aber weder erscheinen die anderen Komponenten auf dem Bildschirm noch reagiert das Programm auf irgendwelche Befehle (Sanduhr...)

Ich habe es Buchstabe für Buchstabe abgetippt und bin mir sicher das es stimmt...

Delphi-Quellcode:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    TrackBar2: TTrackBar;
    TrackBar3: TTrackBar;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    CheckBox3: TCheckBox;
    PaintBox1: TPaintBox;
    PaintBox2: TPaintBox;
    PaintBox3: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Th1: TLineThread;
  Th2: TRectThread;
  Th3: TCircThread;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Th1 := TLineThread.Create(PaintBox1, tpNormal);
  Th2 := TRectThread.Create(PaintBox2, tpNormal);
  Th3 := TCircThread.Create(PaintBox3, tpNormal);
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
var
  tH: TThread;
begin
  if Sender is TTrackBar then
    with TTrackBar(Sender) do begin
      case Tag of
        1: Th := Th1;
        2: Th := Th2;
        3: Th := Th3;
      end;
      case Position of
        1: Th.Priority := tpIdle;
        2: Th.Priority := tpLowest;
        3: Th.Priority := tpLower;
        4: Th.Priority := tpNormal;
        5: Th.Priority := tpHigher;
        6: Th.Priority := tpHighest;
        7: Th.Priority := tpTimeCritical;
      end;
    end;
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
var
  Th: TThread;
begin
  if Sender is TCheckBox then
    with TCheckBox(Sender) do begin
      case Tag of
        1: Th := Th1;
        2: Th := Th2;
        3: Th := Th3;
      end;
      if Th.Suspended then Th.Resume else Th.Suspend;
    end;
end;

end.
Delphi-Quellcode:
unit thread2;

interface

uses Classes, ExtCtrls, Windows;

type
  TPaintThread = class(TThread)
  private
    fX1, fX2, fY1, fY2, fColor: Integer;
    fBox: TPaintBox;
  protected
    procedure Execute; override;
    procedure Paint(x1, y1, x2, y2, Color: Integer);
    procedure DoPaint; virtual; abstract;
  public
    constructor Create(Box: TPaintBox; ThreadPriority: TThreadPriority);
  end;

  TLineThread = class(TPaintThread)
  protected
    procedure DoPaint; override;
  end;

  TRectThread = class(TPaintThread)
  protected
    procedure DoPaint; override;
  end;

  TCircThread = class(TPaintThread)
  protected
    procedure DoPaint; override;
  end;

implementation

uses Graphics;

constructor TPaintThread.Create(Box: TPaintBox; ThreadPriority: TThreadPriority);
begin
  inherited Create(false);
  Priority := ThreadPriority;
  fColor := 0;
  fBox := Box;
  fX1 := 0;
  fX2 := 0;
  fY1 := 0;
  fY2 := 0;
end;

procedure TPaintThread.Execute;
begin
  randomize;
  while true do
    Paint(Random(200), Random(200), Random(200), Random(200), Random($FFFF));
end;

procedure TPaintThread.Paint(x1, y1, x2, y2, Color: Integer);
begin
  fX1 := x1;
  fX2 := x2;
  fY1 := y1;
  fY2 := y2;
  fColor := Color;
  Synchronize(DoPaint);
end;

procedure TLineThread.DoPaint;
begin
  with fBox.Canvas do
  begin
    Pen.Color := fColor;
    MoveTo(fX1, fY1);
    LineTo(fX2, fY2);
  end;
end;

procedure TRectThread.DoPaint;
begin
  with fBox.Canvas do
  begin
    Brush.Style := bsClear;
    Pen.Color := fColor;
    Rectangle(fX1, fY1, fX2, fY2);
  end;
end;

procedure TCircThread.DoPaint;
begin
  with fBox.Canvas do
  begin
    Brush.Style := bsClear;
    Pen.Color := fColor;
    Ellipse(fX1, fY1, fX2, fY2);
  end;
end;

end.
Bin mittlerweile am verzweifeln!! was ist falsch daran??

Dax 23. Jan 2008 14:48

Re: Trotz Threads keine Reaktion
 
Synchronize() sorgt dafür, dass die Aktion im Hauptthread der Anwendung ausgeführt wird. Deswegen wird das Teil so lahm... Lass die Synchronize-Aufrufe mal weg, dann steigt die Wahrscheinlichkeit, das Programm zu crashen, zwar enorm an (vielleicht ist sie sogar 1, ich weiß es nicht mehr), aber der Hauptthread sollte nicht mehr blockieren.

sirius 23. Jan 2008 14:55

Re: Trotz Threads keine Reaktion
 
Ich würde eher ein Kleines Sleep in den Thread einbauen.
Dein Programm hängt wahrscheinlich in dem Classes.Checksynchronize fest, da die Threads schneller sind, als das Zeichnen.
Da ist eine Schleife die solange arbeitet bis kein Synchronize mehr "anliegt" Das passiert aber nicht.
Probier es so:
Delphi-Quellcode:
while true do //besser: while not terminated
begin
    sleep(10); //oder kleiner
    Paint(Random(200), Random(200), Random(200), Random(200), Random($FFFF));
end;

Green 23. Jan 2008 22:26

Re: Trotz Threads keine Reaktion
 
Also wenn ich Synchronize weglasse werde ich mit Exceptions zugeballert...

Das mit Sleep funktioniert nur teilweise, das Programm wird gestartet, und vollständig angezeigt.
Wärend der Sleep zeit (hab se ma auf 10 000 gestellt) ist es auch zu bedienen, aber wenn mehr als 2 Threads mit tpNormal laufen dann lässt sich das Programm GAR NICHT mehr bedienen.

Wenn ein Thread läuft (die anderen wärend den zehn sekunden auf suspend gestellt) dann läuft auch alles Prima, sobald zwei threads am laufen sind stürzt das Programm ab und man mus es abschiessen.

Was machen?

lG Jan

//Edit: hab mir grad das mit dem Synchronize mal nochmal durchgelesen und dabei gefunden:
Man darf aus Threads heraus nicht direkt auf VCL Objekte zugreifen (in dem Fall ja eine Paintbox) also was konkret macht jetzt dieses Synchronize??

Dax 24. Jan 2008 04:20

Re: Trotz Threads keine Reaktion
 
Zitat:

Zitat von Dax
Synchronize() sorgt dafür, dass die Aktion im Hauptthread der Anwendung ausgeführt wird.


sirius 24. Jan 2008 08:03

Re: Trotz Threads keine Reaktion
 
:coder2: Ah, die Sache hat folgenden Haken. In FormCreate existieren deine Paintboxes noch gar nicht. Du dürftest also als Zeiger nil übergeben. Lege die Sachen mal aus FormCreate in das onShow-Ereignis.

In den Methoden, die du mit Synchronize aufrufst (hier: DoPaint) kannst du auf alle Variablen (inkl. der VCL) des Mainthreads zugreifen.

OlafSt 24. Jan 2008 13:03

Re: Trotz Threads keine Reaktion
 
Zitat:

Lege die Sachen mal aus FormCreate in das onShow-Ereignis.
...und dann laß das Synchronize mal weg ;)

Muetze1 24. Jan 2008 13:19

Re: Trotz Threads keine Reaktion
 
Zitat:

Zitat von OlafSt
Zitat:

Lege die Sachen mal aus FormCreate in das onShow-Ereignis.
...und dann laß das Synchronize mal weg ;)

Und warum dies? Sind die Paintboxen dann keine VCL Elemente mehr? Er muss den Zugriff auf die Paintboxen synchronisieren - so oder so.

Lossy eX 24. Jan 2008 15:40

Re: Trotz Threads keine Reaktion
 
@Green: Ich sage es ja nur ungern. Aber das Threadbeispiel ist schlecht oder zu mindest extrem ungünstig. Hier mal ein paar Punkte die ich daran auszusetzen habe.

- Wie du ja selber merkst rauschen die Ereignisse von den Threads so schnell durch, dass sie die Verarbeitung von Windowsbotschaften behindern. Ergo deine Anwendung wird richtig ausgebremmst. Das bringt mich zum zweiten Punkt.

- Die Threads arbeiten mit der VCL / GDI. Daher müssen alle Aufrufe synchronisiert werden. Da gibt es kein vielleicht! In dem Beispiel ist es sogar noch so, dass außer der VCL nichts anderes gemacht wird. Also stehen die Thread zu ca 99% (+-1%) ihrer Zeit in einem Synchronize. Und das ist vollkommen unsinnig bzw. praxisfern. Denn Threads sind dazu da um Arbeiten zu paralelisieren. In dem Falle reihen sich alle nacheinander in einer Schleife ein.

Klar. Man muss zwar mal manchmal synchronisieren aber das sollte auf ein Minimum reduziert werden. Threads leben dadurch, dass sie "selbsttätig" arbeit verrichten. Durch andauerndes Synchronize wird dies verhindert.

- Die Prioritäten als Trackbar ist mit Vorsicht zu genießen. Ich weiß nicht genau wie sich das in dem Fall äußert, wenn man die Priorität auf Echtzeit setzt. Aber in Abhängigkeit der Prozesspriorität wird die Priorität des Threads etwas erhöht. Da gibt es von MS irgendwo eine genaue Liste, denn die Priorität ist eigentlich ein Wert von 1-31 oder so was in dem Dreh. Eine zu hohe Priorität kann auch dazu führen, dass dein Fenster ausgebremmst wird. Und in diesem Beispiel denke ich sogar, dass die unterschiedliche Prioritäten sich nicht wirklich zeigen dürften. Denn die Threads stehen ja eigentlich nur im Synchronize. Und das hat mit dem VCL Thread wieder eine normale Priorität. Also ist die Einstellung der Priorität eigentlich wieder futsch.

- Wurde schon mal erwähnt. Im Execute ein while true do ist ungünstig. Da sollte man lieber mit Terminated arbeiten, da man die Threads ansonsten nur gewaltsam abschießen muss. Und das ist selten Sinn des Erfinders. Zu mal die in deinem Beispiel nicht mal freigegeben werden.

Mein Rat an dich ist also folgender. Stecke nicht zu viel Energie in dieses Beispiel, da es in meinen Augen nicht günstig und für die Praxis absolut nicht brauchtbar ist. Schau dir an was dort gemacht wird aber nimm es nicht zu wörtlich oder übertrage es so in deine Anwendungen.

sirius 24. Jan 2008 16:05

Re: Trotz Threads keine Reaktion
 
@Lossey:
Deine Einwände sind vollends richtig. Aber ich vermute, dass da in mehreren Kapiteln/Abschnitten noch ein bisschen mehr aufgebaut wird. Vielleicht wird ja daraus noch eine Datenbankanwendung oder eine Anwednung mit verteilten Processen.

Green 24. Jan 2008 17:11

Re: Trotz Threads keine Reaktion
 
Zitat:

Zitat von sirius
@Lossey:
Deine Einwände sind vollends richtig. Aber ich vermute, dass da in mehreren Kapiteln/Abschnitten noch ein bisschen mehr aufgebaut wird. Vielleicht wird ja daraus noch eine Datenbankanwendung oder eine Anwednung mit verteilten Processen.

Nein, ist nur ein kleines Beispiel...

Hab mir schon was neues ausgedacht (n Timer marke Eigenbau).
Aber das mit dem Synchronisieren, das hab ich noch nicht geblickt! was genau macht das denn jetzt??

sirius 24. Jan 2008 18:18

Re: Trotz Threads keine Reaktion
 
Synchronisieren
Problem: Da du nie weist wo welcher Thread gerade ist im Programmablauf, kann es passieren, dass mehrere Threads gleichzeit auf denselben Speicherplatz zugreifen. Ungünstigerweise schreibt der eine gerade was, während der andere genau an der Stelle liest. Dadurch entstehen nicht nur Fehler weil die Werte falsch sind, Es kann auch ganz leicht zu Speicherzugriffsverletzungen führen. Wenn zum Beispiel der eine Thread dachte die dynamische Variable existiert noch, während der andere sie grad löscht (und neu anlegt mit anderer Größe).

Lösung:
Nun kann man solche Zugriffe absichern (z.B.) über Critical Sections. Da das aber recht kompliziert ist und wir mit Delphi ja einfach klicken und programmieren wollen, hat man sich synchronize ausgedacht.

Wat macht dat nun:
Das ist ein bisschen tricky. Prinzipiell gibt es eine globale Liste für Methoden, an die jeder Thread etwas anhängen kann. An diese Liste hängt synchronize einfach deine Methode ran. Und jetzt wartet dein Thread (bleibt einfach stehen) erstmal.
Jetzt ist der MainThread drann. Wenn der grade nix besseres zu tun hat, dann schaut er mal, ob in der Liste etwas zum abarbeiten drin steht. Wenn das der Fall ist führt er die Methode aus und löscht sie aus der Liste. Dadurch führst du die Methode, die du synchronize übergeben hast im MainThread durch während dein Thread schläft. Dein Thread wartet nämlich auf ein Signal vom MainThread, dass der Mainthread mit der Methode fertig ist. Das ist alles. Und du hast den Vorteil: du kannst problemlos auf Variablen aus MainThread und aus deinem Thread zugreifen.
Synchronize hat aber auch Nachteile. Einen dürftest du aufgedeckt haben. Die Liste fragt der Mainthread in einer while-Schleife ab: "while List.count>0 do". Das Problem dabei entsteht, wenn es mehrere Threads gibt, die ununterbrochen die Liste füllen. Dadurch kommt der Mainthread aus dieser Schleife nicht mehr raus. Der eine Thread wartet zwar bis seine Methode abgearbeitet ist aber unterdessen füllen die anderen beiden Threads die Liste wieder auf. Dadurch macht der Mainthread nix anderes mehr, als die Liste abzuarbeiten und reagiert auf keine Messages mehr. Kommt also gar nicht zum Zeichnen und interessiert sich auch nicht für sonstige Eingaben.

Es gibt mit synchronize noch andere Probleme, die fallen mir nur grad nicht ein. Die letzten Sätze sollen diese Methode nicht durch den Dreck ziehen, ich wollte nur kurz aufzeigen, dass es zwar schön einfach ist. Aber man sollte den Mechanismus immer bedenken.

Green 24. Jan 2008 21:54

Re: Trotz Threads keine Reaktion
 
Ok, kann man das mit einem Stack vergleichen?

Dann frage ich mich nur warum ich in diesem Fall mit Exceptions zugeballert werde wenn ich Synchronize weglasse? Die Threads greifen doch auf verschiedene Objekte zu?!?

sirius 24. Jan 2008 22:05

Re: Trotz Threads keine Reaktion
 
Ein Stack...ja. Eben eine Liste im FIFO-Prinzip. Und jedesmal wenn ein Thread synchronize aufruft, setzt er der Liste eine Methode hinten an (und zwar die Methode die man übergibt; hier: DoPaint) und dann wartet der Thread bis die Methode vom Mainthread ausgeführt wurde.

Du benutzt zwar verschiedene Paintboxen. Aber die VCL hat dahingehend irgendwo einen Haken. Hier dürfte der Pen- oder der BrushManager der Auslöser sein. Diese sind global und darauf greift jedes Grafikobjekt zu.

mkinzler 24. Jan 2008 22:09

Re: Trotz Threads keine Reaktion
 
Zitat:

Ein Stack...ja. Eben eine Liste im FIFO-Prinzip.
Eher LIFO

sirius 24. Jan 2008 22:15

Re: Trotz Threads keine Reaktion
 
Zitat:

Zitat von mkinzler
Zitat:

Ein Stack...ja. Eben eine Liste im FIFO-Prinzip.
Eher LIFO

Ein Stack ja, aber die SyncListe nicht ;) Deswegen mein zögerndes "Ja"

Edit: Oder muss man da generell Nein sagen, da Stack immer LIFO ist?

Lossy eX 26. Jan 2008 08:47

Re: Trotz Threads keine Reaktion
 
Ein Stack ist Last In First Out. Also es wird etwas oben drauf gepackt und das muss auch als Erstes wieder entfernt werden. (Bücherstapel)

Das Synchronize arbeitet aber mit einer Liste bei der das erste Element entfernt wird und alle anderen durch Add hinzugefügt werden. Also First In First Out. Und das ist eine klassische Liste oder auch Queue. (Warteschlange)

@Green: Knallen tut es unter anderem auch deswegen, weil die Paintboxen zwar unterschiedliche Objekte sind aber diese liegen alle auf dem gleichen Fenster. Und Paintboxen sind im Vergleich zu einem Panel kein echtes Windowsfenster. Sondern "lediglich" eine Kappselung eines Canvas. Damit zeichnen alle Threads auf das gleiche Windowsfenster. Erschwerend kommt hinzu, dass die GDI nur Anwendungsübergreifend sicher ist. Aber innerhalb einer Anwendung nicht Threadsafe ist.

Es muss nicht immer knallen. Ich hatte schon mal das Phänomen, dass er einfach aufgehört hat zu zeichnen. Vollkommen willkührlich. Das macht das Problem auch so unberechenbar und gefährlich.

negaH 26. Jan 2008 09:32

Re: Trotz Threads keine Reaktion
 
Das GDI = Graphics Device Interface ist eine Schnittstelle des Windows-OS das es ermöglicht Bilder in die Grafikkarte zu zeichnen. Wenn nun das GDI nicht threadsafe ist so ist es auch nicht das Windows-Fensterhandling das das GDI benutzzt um den Inhalt der Fenster zu zeichnen und somit ist es auch nicht die VCL threadsafe die auf beide APIs aufsetzt. Ergo: Zeichen/Fensterroutinen niemals in Threads benutzen, und somit ist dein Beispiel von Grund auf fürn Popo. Man kann also diesen "fehler" nicht der VCL in die Schuhe schieben, obwohl sie ihren Teil noch zusätzlich zur Thread-Unsicherheit beiträgt.

Gruß Hagen

rawsoul 31. Jan 2008 23:12

Re: Trotz Threads keine Reaktion
 
Ich denke, Synchronize würde mir bei meinem aktuellen Problem auch helfen. Allerdings ist Synchronize bei mir als Konstante (Integer) definiert, nicht als Methode. Was ist denn hier schiefgelaufen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz