Einzelnen Beitrag anzeigen

Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#11

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