Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Bilder komprimieren und speichern mit Threads (https://www.delphipraxis.net/158734-bilder-komprimieren-und-speichern-mit-threads.html)

Schwedenbitter 28. Feb 2011 17:37

Bilder komprimieren und speichern mit Threads
 
Liebe Mitlesende,

ich stehe vor folgendem Problem und dabei ein bisschen auf dem Schlauch:

Gegeben ist ein Programm, dass über TDelphiTwain einen Seitenscanner ansteuert und pro Scan-Auftrag etwa 25 DIN-A4-Seiten als TBitmap geliefert bekommt. Der Scanner schafft ca. 45 Seiten/min.
Die TBitmaps werden zunächst jeweils in einer TObjectList abgelegt. Erst wenn das letzte Bild gescannt wurde, werden diese alle nacheinander beschnitten, größenangepasst, gedreht, ins PNG-Format komprimiert und dann gespeichert.
Das hat ein paar Nachteile: So belegt das Programm während des Scannes viel, viel Speicher, die CPU wird während des Scannens de facto nicht benutzt etc. pp.

Meine Idee war es daher, das ganze in Threads auszulagern. Allerdings habe ich Probleme damit, wie ich das logisch hinbekommen kann. Ich könnte jeweils einen Thread erzeugen, sobald das nächste Bild geliefert wird. Damit würde ich aber den PC heillos überlasten.
Ich müsste es daher hinbekommen, dass ich die Anzahl auf z.B. 10 beschränke. Wenn also 10 Threads gestartet sind, sollen weitere Bilder nach dem alten Muster wieder in eine Liste kommen. Ist ein Thread fertig (also wieder nur 9), soll ein neuer gestartet werden ...

Meine Fragen:
  1. Bringt das etwas? Unsere Arbeitsplatzrechner haben alle nur 1 CPU/1Kern.
  2. Wie mache ich das Grundsätzlich?
  3. Am einfachsten würde ich das wieder über die TObjectList machen, habe aber skrupel, diese aus dem Thread heraus anzufassen (Bild holen und nach dem Speichern löschen). Ich würde die Threads zählen. Fällt die Zahl unter 10, käme der nächste. Geht das so, wie muss ich das machen? Kann ich den Thread-Zähler aus dem Thread einfach so ändern?
  4. Ich würde die Threads wegen der besseren Lesbarkeit des Codes gern in eine separate Unit auslagern. Dann komme ich aber an die Liste mit den Bitmaps nicht heran und muss die TBitmaps mit übergeben, sodass 3. eigentlich ausscheidet, richtig?

Bitte auch gern Links, wo ich mich zu dem Thema belesen kann.

Gruß, Alex

jfheins 28. Feb 2011 18:05

AW: Bilder komprimieren und speichern mit Threads
 
Zitat:

Ich könnte jeweils einen Thread erzeugen, sobald das nächste Bild geliefert wird. Damit würde ich aber den PC heillos überlasten.
Sicher? Wenn die Verarbeitung ungefähr genauso schnell geht wie die Daten kommen (oder schneller) wäre das eigentlich ein guter Ansatz.

Zitat:

Meine Fragen:
Bringt das etwas? Unsere Arbeitsplatzrechner haben alle nur 1 CPU/1Kern.
Das rechnen fängt eben schon nach der ersten Seite an und nicht erst wenn alle fertig sind. Das gibt dir schonmal einen Vorsprung von 32 Sekunden. Außerdem wird das Programm durch die Parallelisierung von Multicores profitieren, die möglicherweise zukünftig angeschafft werden.

Das mit der Objectlist lässt sich leicht lösen. Solange du auf ein Bitmap nur aus einem Thread zugreifst, ist alles OK. Also im Hauptthread erstellen, das Bitmap an den Thread weitergeben und dort dann zerstören sollte kein Problem sein solange du nach der Übergabe im Hauptthread nichts mehr machst ;)

Schwedenbitter 28. Feb 2011 19:42

AW: Bilder komprimieren und speichern mit Threads
 
Danke für die Antwort.
Zitat:

Zitat von jfheins (Beitrag 1084926)
Also im Hauptthread erstellen, das Bitmap an den Thread weitergeben und dort dann zerstören...

Könntest Du das bitte genauer erklären?

Wenn ich das - bis jetzt - richtig verstanden habe, dann wäre mein Anfang schon falsch,
Delphi-Quellcode:
Constructor TSaveThread.Create(Bitmap: TBitmap; FileName: String;
  Compression: Integer; Resize: Boolean; DPI: Integer; Tolerance: Integer);
Begin
  Inherited Create(True);
  FBitmap:=TBitmap.Create; // <- Blödsinn???
  FBitmap.Assign(Bitmap);  // <- klappt IMHO nur nach .Create
  ...
End;

Procedure TSaveThread.Execute;
Begin
  ...
  FBitmap.Free;
End;
denn so lege ich ein neues TBitmap an und kopiere lediglich das Bild. Und da ich nicht nach dem Motto Trial-and-error arbeiten kann/wollte, habe ich das hier
Delphi-Quellcode:
FBitmap:=Bitmap;
noch nicht probiert. Es ist auch ein bisschen schwierig für mich. Normalerweise mache ich das schon einmal mit Versuch und Irrtum. Hier hält mich aber davon ab, dass ich den umfangreichen Code nicht zu Testzwecken aus dem Programm herausschreiben kann und ich für jeden Test zig Blätter einscannen muss :lol:

Gruß, Alex

jfheins 28. Feb 2011 20:29

AW: Bilder komprimieren und speichern mit Threads
 
Okay, ich versuch das mal:

Also: Solange man nur aus einem Thread auf ein Bitmap zugreift kann eigentlich nichts schiefgehen. Du kannst also im Hauptthread das Bitmap erstellen und befüllen. Dann erstellst du einen Thread (Modus Suspended um sicher zu gehen) und übergibst dem Thread das TBitmap-Objekt. Der Thread "übernimmt" das Objekt (ja, mit FBitmap:=Bitmap;) und gibt es am Ende auch frei. Danach kannst du im Hauptthread die Bitmapvariable auf nil setzen (um auch ja keine Sachen mehr damit zu machen) und den Thread anschließend loslaufen lassen.

Das müsste gehen weil ja immer nur ein Thread zugreift. Ich weis nur nicht genau, ob TBitmap zu der (nicht thread-safe) VCL gehört. (Eigentlich wird ja nichts angezeigt...)
Laut dieser Diskussion: http://qc.embarcadero.com/wc/qcmain.aspx?d=43018 könnte es sein, dass du das Canvas locken musst, damit alles gut geht.

Bummi 28. Feb 2011 20:33

AW: Bilder komprimieren und speichern mit Threads
 
Zitat:

dass du das Canvas locken musst, damit alles gut geht
kann ich aus leidvoller Erfahrung bestätigen.

Schwedenbitter 28. Feb 2011 22:33

AW: Bilder komprimieren und speichern mit Threads
 
Zitat:

Zitat von Bummi (Beitrag 1084954)
Zitat:

dass du das Canvas locken musst, damit alles gut geht
kann ich aus leidvoller Erfahrung bestätigen.

Wie jetzt? Auch bei der puren Übergabe an den Thread? Ich zeichne nichts auf das Canvas innerhalb eines Thread.
Delphi-Quellcode:
Procedure TScan.TwainTwainAcquire(Sender: TObject;
   Const Index: Integer; Image: TBitmap; Var Cancel: Boolean);
Begin
End;
Mit dieser Procedure kommt das Bild an. Image wird automatisch verworfen/überschrieben oder sonstwas, wenn die Procedure wieder beendet wird. Ich starte meinen Thread genau dort und übergebe das TBitmap.

Ich habe leider keinen Scanner hier, werde es aber morgen dann mal probieren.

Danke, Alex

Medium 28. Feb 2011 22:35

AW: Bilder komprimieren und speichern mit Threads
 
@Bummi: Wie sinnfrei ist das denn? Also, gut möglich, aber wenn die einzige Referenz im Thread liegt, warum sollte ich dann noch was locken müssen? Klingt für mich nach einer ziemlichen VCL-Nachlässigkeit bzw. Konzeptfehler. Ein Grund mehr, warum ich sowohl in Delphi als auch .NET praktisch grundsätzlich mit eignen Bitmapklassen arbeite, die ihre Daten in hübschen eindimensionalen, performaten Arrays halten. Der ganze Overhead in den diversen Grafikroutinen geht mir langsam auf die Nüsse... aber das nur so nebenbei, und ist ja nicht dein Verschulden =)

Was ich gern mache, ist einem Thread eine List<TAbzuarbeitendeObjektklasse> zu verpassen. Im Thread dann:
Delphi-Quellcode:
procedure Execute;
var
  ref: TAbgearbeiteteObjektklasse;
begin
  repeat
    if MyTaskList.Count>0 do
    begin
      ref := PerformTask(MyTaskList[0]);
      MyTaskList.Delete(0);
      SendMessage(hwndMyMainForm, WM_MYOBJECT_READY, Integer(ref), 0); // falls nötig, dass der MainThread die Fertigstellung mitbekommen muss
      // wobei ddas nicht ganz sauber ist, der MainThread muss dann gesichert "ref" verarbeitet oder kopiert haben, bevor das neue "ref" fertig ist.
      // Da ggf. eine weitere Queue nehmen, oder mit Flags arbeiten.
    end;
    Sleep(1);
  until Terminated;
end;
und im Hauptthread dann
Delphi-Quellcode:
procedure MyNewDataHandler(aObjectToHandle: TAbzuarbeitendeObjektKlasse);
begin
  MyThread.MyTaskList.Add(aObjectToHandle);
end;
Und wenn die zu verarbeitenden Objekte selbst aus einem Thread stammen, bekommt der einen Callback, der o.g. Prozedur auslöst, was wahlweise auch per Windows Message hübsch sein kann. Dadurch gibts dann einen Thread, der ständig auf neue Inputs wartet, und wenn etwas kommt es fix durchrattert, so schnell wie es eben geht, und dann einfach mal fertig damit ist. Die Ergebnisse aus dem Verarbeitungsthread könnten dann ggf. in gleicher Weise an einen Speicher- oder Netzwerk-Thread übergeben werden, der dann entsprechend weiter macht. Im Grunde einfaches Queuing nach FIFO Prinzip. Macht sich für so Arbeitsthreads ganz nett.

\\Edit: Hupps, Code war LIFO, sollte FIFO sein :>

\\Edit2: Natürlich kann man PerformTask() wieder einen neuen Thread auslösen lassen, wodurch man je nach Struktur noch mehr von Multicores profitieren würde, wenn die eigentliche Abarbeitung single-threaded ist. Seit Multicores so langsam Standard sind, nutz ich solche Modelle zumindest ausschweifend =). Dann ändert sich aber natürlich die Stelle fürs Benachrichtigen des Hauptthreads.

\\Edit3: Schleife geändert... zu viel C# in letzter Zeit, ich bring alles durcheinander =)

Bummi 28. Feb 2011 23:24

AW: Bilder komprimieren und speichern mit Threads
 
Zitat:

Wie sinnfrei ist das denn?
ich sprach lediglich über meine Erfahrungen, mehrere Threads die auf das Canvas der übergebenen Bitmaps zugegriffen haben, jeder sein eigenes Bitmap, keines der Bitmaps war irgendwo sichtbar, trotzdem gab es ohne Canvas.Lock ca. 50 % Artefakte.

Medium 28. Feb 2011 23:33

AW: Bilder komprimieren und speichern mit Threads
 
Aha okay! Wenn mehrere Threads auf den Canvas zugreifen, dann ist's ja sonnenklar. Dann gehört das auch so :)

Schwedenbitter 1. Mär 2011 10:49

AW: Bilder komprimieren und speichern mit Threads
 
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für Eure vielen Hinweise!

Ich habe mal angefangen und das Ergebnis sieht schon ganz gut aus. Auf mehrere Threads habe ich verzichtet, da wir hier in absehbarer Zeit sowieso vermutlich nie Rechner mit mehreren Kernen haben werden und es so einfacher zu strukturieren war. Ich habe meinen Code mal als Datei angehängt und würde mich freuen, wenn jemand noch gute Hinweise hat. Canvas muss erwartungsgemäß nicht gelocked werden.

Mir geht es insbesondere um das Aufräumen. Ich wollte mich nicht um den Thread kümmern müssen und habe daher
Delphi-Quellcode:
FreeOnTerminate:=True;
gesetzt. Aus diesem Grund muss ich
Delphi-Quellcode:
TObjectList
am Ende von
Delphi-Quellcode:
TThread.Execute;
freigeben, weil der Destructor so nicht aufgerufen wird, in dem ich das sonst getan hätte.
Ferner bin ich mir nicht sicher, ob ich innerhalb des Thread für das Hinzufügen bzw. Löschen von Bildern mit einer CriticalSection arbeiten muss/sollte. Es wäre ja sonst denkbar, dass in exakt demselben Moment mit nicht absehbaren Folgen ein Bild hinzugefügt und ein anderes gelöscht wird.

Den Code habe ich mit FastMM geprüft. Speicherlecks wurden keine angezeigt :-D

Gruß, Alex

Bummi 1. Mär 2011 12:18

AW: Bilder komprimieren und speichern mit Threads
 
zumindest Append müsstest Du mit einer CS kapseln ...
ansonsten habe ich so ein komisches Bauchgefühl ...

Schwedenbitter 1. Mär 2011 23:20

AW: Bilder komprimieren und speichern mit Threads
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zu früh gefreut:

Ich habe meinen Code jetzt mal unter realistischeren Bedingungen getestet. Dazu habe ich jeweils zwischen
Delphi-Quellcode:
Append();
ein
Delphi-Quellcode:
Sleep();
gepackt. Hintergrund ist der, dass der Scanner die Bilder auch nicht sofort auf einmal liefert.
Zudem habe ich den Thread zur besseren Lesbarkeit in eine eigene Unit gepackt.

Dabei ist mir ein weiterer Fehler aufgefallen.
Der Thread wird offenbar noch korrekt gestartet. Auch wenn das Ereignis an das MainForm erst später kommt, so sehe ich zeitnah nach dem Erstellen des Thread die 1. Datei auf dem Laufwerk erscheinen. Dann passiert aber lange nichts. Der Rest wird erst in die Dateien gespeichert, wenn alle Dateien geliefert wurden. Das ist aber leider nicht der Sinn vom Ganzen. Nach meinem Verständnis sollte der Thread, solange nicht
Delphi-Quellcode:
Ende:=True;
gesetzt ist, alles sofort wegspeichern, was in der Liste ist... :gruebel:

Ich habe die Änderungen vom Code wieder als zip-Datei angefügt und würde mich freuen, wenn mal jemand von den Profis drüber schauen und mir sagen könnte, wo mein Fehler liegt.

Gruß & Dank, Alex

Bummi 2. Mär 2011 06:48

AW: Bilder komprimieren und speichern mit Threads
 
Ich habe nicht alles nachvollzogen, aber ein paar Sachen sind mir aufgefallen:
1.) keine CS bei Append und Ende
2.) in diesem Block
Delphi-Quellcode:
FThread.Append(Bild);
Sleep(250);// -> 4 Bilder / 1 Sek.
läuft der Thread ja bereits.....
3.) wenn das:
Delphi-Quellcode:
Procedure TMainForm.RepeaterTimer(Sender: TObject);
Begin
   Repeater.Enabled:=False;
   ScannenClick(nil);
End;
ausgeführt wird, wird jedes mal eine neuer Thread losgetreten

Vielleicht schaut Sir Rufo mal drüber ....

Schwedenbitter 2. Mär 2011 08:38

AW: Bilder komprimieren und speichern mit Threads
 
Erstmal danke fürs Drüberschauen!

Zitat:

Zitat von Bummi (Beitrag 1085287)
1.) keine CS bei Append und Ende

Das kommt noch. Ich hatte es bereits drin, wollte aber wirklich jede mögliche Fehlerquelle für das beschriebene Phänomen ausschließen.

Zitat:

Zitat von Bummi (Beitrag 1085287)
2.) in diesem Block
Delphi-Quellcode:
FThread.Append(Bild);
Sleep(250);// -> 4 Bilder / 1 Sek.
läuft der Thread ja bereits.....

Eben!
Auf meinem Rechner braucht er allein für das Umwandeln in png ca. 1 Sek/Bild. Er sollte an diese Stelle daher bereits genug zu tun haben, um noch während des Anfügens (
Delphi-Quellcode:
Append();
)weiterer Bilder die bereits vorhandenen umzuwandeln.
Das macht er aber nicht, was ich wiederum nicht verstehe.

Zitat:

Zitat von Bummi (Beitrag 1085287)
3.) wenn das:
Delphi-Quellcode:
Procedure TMainForm.RepeaterTimer(Sender: TObject);
Begin
   Repeater.Enabled:=False;
   ScannenClick(nil);
End;
ausgeführt wird, wird jedes mal eine neuer Thread losgetreten

Das ist ausdrücklich so gewollt!
Ich hatte ebenfalls bereits meine Code fürs Vergrößern/Verkleiner und/oder Drehen mit drin. Da erhielt ich hin und wieder - aber leider nicht wirklich reproduzierbar - eine
Delphi-Quellcode:
EOutOfResources
. Ich habe das Code-Fragment daher mal eine Weile ohne Benutzereingaben laufen lassen.
Man kann den Kreislauf ja jederzeit unterbrechen, indem man einfach den Haken rausnimmt. Dann hört der "Spuk" nach dem nächsten Thread-Ende auf!

Zitat:

Zitat von Bummi (Beitrag 1085287)
Vielleicht schaut Sir Rufo mal drüber ....

Das wäre wirklich nett.

Gruß, Alex

Schwedenbitter 2. Mär 2011 17:11

AW: Bilder komprimieren und speichern mit Threads
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich bin wieder ein Stück weiter!

Die von mir zur Steuerung des Scanners verwendete Komponente
Delphi-Quellcode:
TDelphiTwain
liefert die Bilder immer per Ereignis an das Hauptprogramm. Ich habe dieses Verhalten nun nachgebaut, indem ich die Bilder nicht wie bisher mittels Schleife an den Thread übergebe, sondern per
Delphi-Quellcode:
TTimer.OnTimer;
. Dieses hängt nun - einem sehr schnellen Scanner gleich - aller 250 ms ein weiteres Bild an die Liste im Thread und das klappt jetzt:
Die Meldung "Alle Bilder geliefert..." kommt regelmäßig so ca. nachdem 17 Seiten bereits gespeichert wurden :lol: So war der Plan!

Dennoch finde ich das ein eigenartiges Verhalten. Scheinbar wird der Thread tatsächlich erst gestartet, nachdem die Procedure, in dem er erzeugt wurde, vollständig beendet ist. Das hätte ich nicht vermutet.
Kann jemand diese Beobachtung zumindest für TurboDelphi bestätigen?

Nachdem ich das nun - scheinbar endgültig - geklärt habe. Werde ich mich nun wieder den Problemen Stretchen und Drehen zuwenden.
Das hatte ich bereits getan, bevor ich merkte, dass das Speichern tatsächlich nicht so läuft, wie es soll. Ich hatte dabei mehrfach eine Exception
Delphi-Quellcode:
EOutOfResources
gefangen und würde hierzu ggf. gern mal nachfragen.

@Moderator
Soll ich dazu dann ein neues Thema aufmachen oder hier weiterschreiben?
Ich habe die aktuelle Version mal für die Nachwelt angefügt. Die vorhergehenden Code-Beispiele können bei Platzmangel ggf. gelöscht werden. Danke.

Gruß & Dank, Alex

Blup 3. Mär 2011 07:30

AW: Bilder komprimieren und speichern mit Threads
 
Der Zugriff auf FBilder muss durch eine Critical Section abgesichert werden.

Delphi-Quellcode:
self.Priority:=tpIdle
;
Natürlich macht der Thread so nur was, wenn der Hauptthread gerade nichts zu tun hat.
Beim Erzeugen des Formulars ist der Hauptthread aber schon beschäftigt.

Schwedenbitter 4. Mär 2011 08:51

AW: Bilder komprimieren und speichern mit Threads
 
Zitat:

Zitat von Blup (Beitrag 1085506)
Der Zugriff auf FBilder muss durch eine Critical Section abgesichert werden.

Na gut, na gut. Das habe ich nun gemacht.

Zitat:

Zitat von Blup (Beitrag 1085506)
Delphi-Quellcode:
self.Priority:=tpIdle;
Natürlich macht der Thread so nur was, wenn der Hauptthread gerade nichts zu tun hat.
Beim Erzeugen des Formulars ist der Hauptthread aber schon beschäftigt.

Das verstehe ich nicht ganz. Vom Grundsatz her ist mir das klar. Aber der Thread wird doch erst erzeugt und gestartet, wenn man auf den Button "Scannen" klickt. Da ist doch das Formular bereits erzeugt worden, oder wo ist mein Denkfehler.

Es läuft jetzt übrigens auf einem Testrechner seit mehr als 2 Stunden problemlos. Sorgen bereitet mir nur, dass FastMM mir 3 - wenn auch mit 620 Bytes sehr kleine - Speicherlecks bringt. Ich habe das Programm bereits mit Debug-DCU, map-Dateien etc. pp. compiliert. Allerdings kommen immer Meldungen mit TButton, TDialog usw. Die werden zwar vom Hauptprogramm verwendet. Ich dachte aber, dass sich der Compiler bei entsprechenden Komponenten selbst um die Freigabe kümmert...
Ich würde - falls noch jemand Muße hat - den aktuellen Code nochmal hochladen.

Am Schluss noch eine allgemeine, vielleicht auch marginale Frage:
Im Moment schicke ich vom Thread Ereignisse an den HauptThread und zwar so:
Delphi-Quellcode:
Procedure TSaveThread.MessageNext;
Begin
  If Assigned(FOnNext) Then
    FOnNext(self, FNext);
End;

Procedure TSaveThread.Execute;
Begin
  While (Not Terminated) Do
  Begin
    ... // <- tu irgendwas

    Syncronize(MessageNext);
  End;
End;
Kann man das bedenkenlos auch so machen:
Delphi-Quellcode:
Procedure TSaveThread.MessageNext;
Begin
  FOnNext(self, FNext);
End;

Procedure TSaveThread.Execute;
Begin
  While (Not Terminated) Do
  Begin
    ... // <- tu irgendwas

    If Assigned(FOnNext) Then
      Syncronize(MessageNext);
  End;
End;
Dann würde er nämlich nicht erst Syncronize aufrufen und in die andere Procedure springen müssen, wenn er vorher prüft, ob das Ereignis überhaupt zugewiesen ist.
Die 1. Variante habe ich mehrfach gesehen und gehe deshalb davon aus, dass sie richtig ist. Spricht etwas gegen die 2. Variante?

Gruß, Alex

Bummi 4. Mär 2011 10:01

AW: Bilder komprimieren und speichern mit Threads
 
Wenn Du den Setter von FOnNext mit einer CS verriegelt hast spricht IMHO nichts dagagen, sonst könnte bei (wirst Du nicht machen) gleichzeitigen Zugriff Müll in FOnNext stehen.

Medium 4. Mär 2011 22:45

AW: Bilder komprimieren und speichern mit Threads
 
Wenn es nicht notwendig ist, dass diese "Message" synchron zum (bzw. im) Thread läuft, kannst du auch tatsächliche Windows Messages an dein Fensterhandle schicken, und dort mit einem Eventhandler reagieren. (MSDN-Library durchsuchenSendMessage()) Das würde das ganze noch eine Spur mehr entkoppeln, ist aber daran gebunden, dass im Handler weder Thread auf MainForm noch MainForm auf Thread Felder zugreifen. So angelegte Konzepte "behagen" mir in der Regel mehr, als harte synchrone Callbacks, zumal man mit zu vielen letzterer den Nutzen von Threads auch gerne mal schmälern bis vernichten kann.

Blup 7. Mär 2011 08:43

AW: Bilder komprimieren und speichern mit Threads
 
Im Prinzip ist diese Lösung zulässig:
Delphi-Quellcode:
    CS.Enter;
    try
      If Assigned(FOnNext) Then
        Syncronize(MessageNext);
    finally
      CS.Leave;
    end;
Aber man muss dann auch den Zugriff aus dem Hauptthread auf das Property "OnNext" mit CS schützen.
Damit würde aber in der Ereignisbehandlung "OnNext" im Hauptthread bei einem eventuellen Zugriff auf das Property "OnNext" des SaveThread ein Deadlock entstehen.

In der ursprünglichen Variante ist ein Schutz nicht notwendig, da auf FOnNext grundsätzlich nur im Hauptthread zugegriffen wird.
Ich denke es spricht hier aber nichts dagegen, auf "Assigned(FOnNext)" zusätzlich auch im Thread ohne CS zu prüfen, um unnötige Aufrufe von "Syncronize" zu vermeiden.


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

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