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)