AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Threads und TBitmaps

Ein Thema von TheGroudonx · begonnen am 12. Aug 2014 · letzter Beitrag vom 10. Okt 2017
Antwort Antwort
Whookie

Registriert seit: 3. Mai 2006
Ort: Graz
446 Beiträge
 
Delphi 10.3 Rio
 
#1

AW: Threads und TBitmaps

  Alt 12. Aug 2014, 18:31
Ups ... Delphi 7 ... soweit ich weiß hat sich gerade in den Thread-Klassen einiges getan....
was hier das Problem ist, kann ich so leider nicht sagen...

Ich würde dir empfehlen das ganze mal auszubauen und zu sehen wo das Problem liegt:

Delphi-Quellcode:
procedure TImageRenderer.Execute;
var
  ix: Integer;
  iy: Integer;
begin
  while Not Terminated do
  begin
    for ix := 0 to 99 do
    begin
      for iy := 0 to 99 do
      begin
      end;
      Sleep(5);
    end;
    Synchronize(PaintBmp);
  end;
end;
sollte mal laufen (aber erst mal ohne OnPaint zu verbinden)
Whookie

Software isn't released ... it is allowed to escape!
  Mit Zitat antworten Zitat
TheGroudonx

Registriert seit: 21. Mai 2014
44 Beiträge
 
#2

AW: Threads und TBitmaps

  Alt 30. Aug 2014, 23:59
Tut mir Leid für die lange Zeitspanne bis zur Antwort,

ich habe mittlerweile herausgefunden, dass der Aufruf aus dem Thread heraus das Problem ist.
Er soll auf der Form das Bild zeichnen, was zu dem Fehler führt.
Wird im Thread jedoch nur die Berechnung der Threadeigenen Bitmap durchgeführt und diese durch die Form per Canvas.draw kopiert, so funktioniert es fehlerfrei. Zwar erzeugt das wiederum Auslastung, welche eigentlich im Thread sein sollte, jedoch scheint es sich nicht vermeiden zu lassen.

Edit: Die Threadeigene Bitmap muss im Canvas unlocked werden, damit das Bild auf der Form nicht einfriert

Geändert von TheGroudonx (31. Aug 2014 um 00:01 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Chemiker
Chemiker

Registriert seit: 14. Aug 2005
1.859 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Threads und TBitmaps

  Alt 31. Aug 2014, 10:18
Hallo TheGroudonx,

man sollte auf jeden Fall den Canvas.lock und Canvas.unlock in einem Schutzblock fassen um die Ausgabe wieder freizugeben, die exklusiv für den Thread geblockt worden ist.
Bei der Ausgabe sollte man bedenken, dass die Grafikausgabe vom BS gepuffert wird, dass bedeutet das wenn der Thread vor der Ausgabe schlafen gelegt wird, keine Ausgabe erfolgt. Mit der Function GdiFlush() kann die Ausgabe erzwungen werden.

Bis bald
Chemiker
wer gesund ist hat 1000 wünsche wer krank ist nur einen.
  Mit Zitat antworten Zitat
TheGroudonx

Registriert seit: 21. Mai 2014
44 Beiträge
 
#4

AW: Threads und TBitmaps

  Alt 1. Sep 2014, 11:42
Hallo,

ich verzweifle so langsam an dem Thema Threads...
zwar wird ein Bild ausgegeben, aber es gibt so viele mehr oder weniger zufällig auftretende Fehler...

-Zuerst einmal funktioniert das Schritt-für-Schritt kompilieren des Threads nicht wirklich:
Ein normal durchlaufender Thread bricht beim Schritt-für-Schritt durchgehen zufällig nach ung. 20 durchgängen ab und ist nichtmehr startbar, oder er gibt access violation messages wieder, die sonst nicht kämen, obwohl die Elemente existieren müssten.

-Versuche ich eine Bitmap in ihrer Größe zu verändern, so kommt es dadurch zu einer EOutofrecource-Fehlermeldung. Selbst wenn man die Bitmap auf ihre eigene Größe anpasst, also keine Veränderung, geht das komplette Bild verloren - Was passiert denn da im Thread? Erzeugt er etwa jedesmal eine neue Bitmap, wenn man die Größe ändert, und müllt den Speicher mit den alten Kopien voll?

-Letztendlich ist das Unlocken für mich noch relativ unklar...muss eine Bitmap bei jedem Zeichnen/Prozeduraufruf neu unlocked werden? Ein Projekt von mir Brach ab, obwohl nach dem Create alle Bitmaps unlocked wurden. Der Fehler lies sich nach Debuggen beheben, indem der unlockbefehl einer Bitmap nach create DOPPELT, also HINTEREINANDER!!!, geschrieben wurde.

Das ganze Thema Threads scheint durch und durch voller Fehler zu stecken, es macht kaum Spaß sich den Weg aus denselben zu bahnen...
Jedenfalls in meiner Delphi 7 Version.

Wenn ihr Tipps zu den geschilderten Ereignissen habt die etwas Licht ins Dunkle bringen lasst es mich wissen.
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.688 Beiträge
 
Delphi 2007 Enterprise
 
#5

AW: Threads und TBitmaps

  Alt 1. Sep 2014, 12:46
Threads funktionieren, auch in Delphi 7, tadellos. Man muss sie nur zu nehmen wissen

Zitat:
-Zuerst einmal funktioniert das Schritt-für-Schritt kompilieren des Threads nicht wirklich:
Ein normal durchlaufender Thread bricht beim Schritt-für-Schritt durchgehen zufällig nach ung. 20 durchgängen ab und ist nichtmehr startbar, oder er gibt access violation messages wieder, die sonst nicht kämen, obwohl die Elemente existieren müssten.
Du meist vermutlich das Durchsteppen beim Debuggen, nicht Kompilieren. Dies geht eigentlich wunderbar, zumindest so lange nur eine Instanz des Threads läuft. (Sonst springt man mit den Breakpoints wild zwischen den Instanzen hin und her.) Wenn dort AVs passieren, dann deswegen, weil du dort etwas verkehrt machst. Dies kann u.U. ohne Debuggen laufen, weil dann der Speicher nicht durch den Debugger beeinflusst wird, und die Timings in deinem Programm anders sind.

Zitat:
-Versuche ich eine Bitmap in ihrer Größe zu verändern, so kommt es dadurch zu einer EOutofrecource-Fehlermeldung. Selbst wenn man die Bitmap auf ihre eigene Größe anpasst, also keine Veränderung, geht das komplette Bild verloren - Was passiert denn da im Thread? Erzeugt er etwa jedesmal eine neue Bitmap, wenn man die Größe ändert, und müllt den Speicher mit den alten Kopien voll?
Ja klar, wie sonst?

Zitat:
-Letztendlich ist das Unlocken für mich noch relativ unklar...muss eine Bitmap bei jedem Zeichnen/Prozeduraufruf neu unlocked werden? Ein Projekt von mir Brach ab, obwohl nach dem Create alle Bitmaps unlocked wurden. Der Fehler lies sich nach Debuggen beheben, indem der unlockbefehl einer Bitmap nach create DOPPELT, also HINTEREINANDER!!!, geschrieben wurde.
Locken und Unlocken sollte so gehen: Sobald irgendwer etwas mit dem Bitmap macht, muss es vorher gelocked werden, und nachher unlocked. Nach dem Erstellen sind Bitmaps von "natur" aus nicht gelocked. JEDER Zugriff auf die Bilddaten des Bitmaps sollte in etwa so aussehen:
Delphi-Quellcode:
Bitmap.Canvas.Lock;
try
  Bitmap.Canvas.Rectangle(blablafoofoo);
finally
  Bitmap.Canvas.Unlock;
end;
Das betrifft auch Größenänderungen und einfach ALLES was am Bild etwas verändern könnte. Ich persönlich würde diese Dinge sogar noch zusätzlich über eine TCriticalSection absichern. (CriticalSections sind praktisch das gleiche wie Locks, die aber nicht nur ein Bitmap schützen, sondern einen ganzen Block beliebigen Code.)
Das gesamte Thema CriticalSections ist bei der Arbeit mit Threads essenziell. Man muss fein säuberlich ALLE Zugriffe auf von mehreren Threads (das eigentliche Programm ist ja auch ein Thread) benutzten Ressourcen gegeneinander verriegeln, sonst hat man - wie du ja merkst - eine AV-Fabrik.

Auch sollte man sich angewöhnen NIEMALS aus einem Thread direkt Komponenten auf einem Formular zu benutzen. Das geht fast immer schief, es fällt nur manchmal lange nicht auf, da es gehen kann. Wann immer ein Thread eine Änderung an Formularen auslösen soll, ist der (imho) beste Weg den Formularen mittels eigener Messages und PostMessage() die Änderungen mitzuteilen, und die eigentliche Versorgung der Controls passiert komplett im Kontext des VCL-Threads (=Hauptprogramm). Alles andere wird über kurz oder lang zu einer Synchronisations-Hölle.


Noch ein Tipp zu Exceptions in Threads: Normalerweise wird man auf Exceptions im Hauptprogramm durch ein entsprechendes Fester hingewiesen (wenn man sie nicht manuell anderweitig behandelt). Dafür ist der Standard-Exceptionshandler von Delphi (bzw. der VCL) zuständig, und der greift bei Threads nicht. Wenn in einem Thread eine Exception passiert, schießt Windows den Thread einfach weg.
Um dies zu verhindern, und um dennoch zu sehen WAS da schief gelaufen ist, baue ich ganz gerne etwas dieser Art:
Delphi-Quellcode:
type
  TThreadException = record
    Message: String;
  end;
  PThreadException = ^TThreadException;

const
  WM_THREAD_EXCEPTION = WM_USER + (irgendeinezahl);

implementation

procedure TMyThread.Execute;
var
  ex: PThreadException;
begin
  try
    // Mein Thread-Code
  except
    on e: Exception do
    begin
      New(ex);
      ex^.Message := e.Message;
      PostMessage(WM_THREAD_EXCEPTION, MainFormHandle, Integer(ex), 0);
    end;
  end;
end;

// Mainform

type
  TMainForm = class(TForm)
    procedure ThreadExceptionHandler(var msg: TMessage); message WM_THREAD_EXCEPTION:
    ...
  end;

implementation

TMainForm.ThreadExceptionHandler(var msg: TMessage);
var
  ex: PThreadException;
begin
  ex := PThreadException(msg.LParam);
  Memo1.Lines.Add(ex^.Message);
  Dispose(ex);
end;
Damit laufen in meinem Memo immer brav die ganzen Exceptions im Thread ein, und da die Exceptions im Thread dann behandelt werden, knallt Windows diesen auch nicht sofort weg.

Fazit: Man kann in Threads nicht mit dem warmen weichen Komfort, den man von der VCL gewöht ist so unbedarft arbeiten. Man muss deutlich mehr aufpassen, und einige Dinge zu Fuß erledigen. Die Threads an und für sich funktionieren einwandfrei. Ich gebe aber zu, dass es etwas Eingewöhnungszeit und ein etwas anderes Denken braucht.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
TheGroudonx

Registriert seit: 21. Mai 2014
44 Beiträge
 
#6

AW: Threads und TBitmaps

  Alt 1. Sep 2014, 13:19
Ich habe jetzt herausgefunden, dass es zu Fehlern kommt, da im Schritt-für-Schritt Modus nur der Thread angehalten wird.
Die Hauptunit greifft nach wie vor im hohen Intervall auf ein Bild zu, um es zu malen, wodurch es bei Größenänderungen zu Fehlermeldungen kommt.
Jetzt muss ich nurnoch herausfinden, wieso das geschieht, obwohl die Bitmap deren Größe geändert wird nicht dieselbe ist wie die, die gemalt wird

Der Code hier scheint den EOutofresource-Fehler aka "Bitmap wird bereits benutzt" zu umgehen.
Vermutlich kann man das auch durch Synchronisierung vermeiden.

Delphi-Quellcode:
Procedure TForm1.ZeichneBild;
begin
image1.Canvas.Draw(0,0,PaintThread.Getbild);
PaintThread.SetExport;
end;

procedure TPaintThread.Execute;
begin

 while (Terminated = False) do
 begin

  if (Paint = True) then //Soll gezeichnet werden?
  begin

  Bearbeitend := True;

   if (Exportierend = False) then //Wird exportiert?
   begin

   MyBild.canvas.unlock; //Nicht sicher ob nötig
   Mybild.Width := random(5000) + 5000; //Fehlerquelle
   Mybild.Height := random(5000) + 5000; //Fehlerquelle
   MyBild.Canvas.Rectangle(0, 0, random(999) + 1, random(999) + 1);

   Bearbeitend := False;
   Paint := False;

   end
   else
   begin

   Bearbeitend := False;

    While (Exportierend = True) do
    begin
    //Bis das Bild fertig exportiert wurde
    end;
   end;
  end
  else
  sleep(1);

 end;
end;


function TPaintThread.Getbild : TBitmap;
begin
Exportierend := True;

 if (Bearbeitend = False) then //Wird Bild bearbeitet?
 begin

 MyBild.Canvas.Unlock; //Nicht sicher ob nötig
 result := MyBild;

 end
 else
 begin

 Exportierend := False;

  While (Bearbeitend = True) do
  begin
  //Warte, bis die Bearbeitung abgeschlossen ist
  end;

 result := Getbild;

 end;
end;

procedure TPaintThread.SetExport;
begin
Exportierend := False;
Paint := True;
end;
  Mit Zitat antworten Zitat
TheGroudonx

Registriert seit: 21. Mai 2014
44 Beiträge
 
#7

AW: Threads und TBitmaps

  Alt 1. Sep 2014, 13:26
Danke für die hilfreichen Infos, Medium.

Ich hatte bis jetzt die Erfahrung, dass Bitmaps, die vom Thread erstellt und benutzt werden, Fehlermeldungen hervorrufen können, was durch .unlock vor dem Zeichnen im Thread nichtmehr geschah. Von daher sollte die Bitmap entweder nicht standardmässig auf unlocked stehen oder die routine macht noch andere relevante dinge. Jedenfalls werde ich das locken ausprobieren.

mfg
Groudonx
  Mit Zitat antworten Zitat
TheGroudonx

Registriert seit: 21. Mai 2014
44 Beiträge
 
#8

AW: Threads und TBitmaps

  Alt 1. Sep 2014, 13:41
Wenn ich nun statt einem rechteck eine bitmap malen will, sollte logischerweise die Malfläche wieder gelockt werden.
Das zu malende Bild muss aber unlocked werden, oder?

Delphi-Quellcode:
 MyBild.Canvas.lock;
 nochnbild.Canvas.unlock;
  
 MyBild.Canvas.Draw(0,0,nochnbild);

 nochnbild.Canvas.lock;
 MyBild.Canvas.unlock;
Dieser Code führt dazu, dass mir das Programm nach Sekunden permanent einfriert.
Entferne ich jedoch
 nochnbild.Canvas.lock; so läuft es prima.
Meine Frage ist also, ob locken wirklich sinnvoll ist bzw. wieso es in dem Fall diesen extrem negativen Fehler hat, durch den das ganze Programm unbrauchbar wird.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#9

AW: Threads und TBitmaps

  Alt 1. Sep 2014, 13:29
So wie du da in den Thread reingreifst, ist das wie Schalten ohne Kuppeln.

Manchmal funktioniert es, manchmal knirscht es und manchmal fliegt dir das Getriebe um die Ohren.

Generell solltest du hier wohl mit Queues arbeiten.
  • In die Eingangsqueue trägst du die Vorgaben ein (Größe, etc.) die der Thread benötigt um das Bild zu erstellen.
  • Der Thread holt sich aus der Queue diese Werte, errechnet das Bild und schiebt das, wenn fertig in die Ausgangsqueue rein.
  • Zum Anzeigen holst du einfach die Bilder aus der Ausgangsqueue ab.
Das Befüllen und Auslesen der Queues muss natürlich mit CriticalSections abgesichert werden.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Antwort Antwort

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:55 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