![]() |
Delphi-Version: 5
Synchronize mit Argumenten
Moin.
Erstmal habe ich schon gelesen, dass das was ich im Titel geschrieben habe nicht zu gehen scheint. Mal ein Beispiel für mein Problem: Ich habe in einem Thread in dem z.B. irgendwelche Fehler auftreten können. Jetzt möchte ich meinem Hauptthread je nach aufgetretenem Fehler verschiedene Strings über geben. Soweit bin ich schon:
Delphi-Quellcode:
type
TInstError = procedure(const Content: String) of object; TMyOwnThread = class(TThread) private FInstError: TInstError; procedure SyncError; public property InstError: TInstError read FInstError write FInstError; end; implementation procedure TMyOwnThread.SyncError; begin if Assigned(FInstError) then FInstError('ErrorString'); end; procedure TMyOwnThread.Execute; begin ... // Error 1 Synchronize(SyncError); ... // Error 2 Synchronize(SyncError); ... // Error 3 Synchronize(SyncError); usw. end; procedure Form1.InstError(const Content: String); begin RichEditError.Lines.Add(Content); end; So müsste ich aber für jeden Fehler einen eigene Methode erstellen welche ich mit Synchronize aufrufe. Ich finde es schon lästig, dass ich überhaupt eine zusätzlich Methode dafür brauche. Das fördert nicht gerade die Übersichtlichkeit. Aber wenn ich für jeden String ne eigene Methode brauch werd' ich ja bekloppt :pale: Wie kann ich das eleganter lösen? Vielen Dank |
AW: Synchronize mit Argumenten
Spendier doch dem Thread ein privates Stringfeld, in das Du die Fehlermeldung schreibst. Dieses übergibst Du dann mittels FInstError.
[edit] *Hehe* schneller :) [/edit] |
AW: Synchronize mit Argumenten
Delphi-Quellcode:
procedure TMyOwnThread.SyncError;
begin if Assigned(FInstError) then FInstError(FError); end; procedure TMyOwnThread.Execute; begin ... // Error 1 FError := 'Error 1' ; Synchronize; ... // Error 2 FError := 'Error 2' ; Synchronize; usw. end; |
AW: Synchronize mit Argumenten
Tja, das ist mit der Delphi-Version blöde ... bei den neueren Versionen macht man das ganz charmant über Anonyme Methoden.
Aber so sollte es auch charmant gehen:
Delphi-Quellcode:
type
TInstError = procedure(const Content: String) of object; TMyOwnThread = class(TThread) private FInstError: TInstError; // procedure SyncError; @DeddyH: logisch muss die hier raus :o) CallError( const Content : string ); public property InstError: TInstError read FInstError write FInstError; end; implementation procedure TMyOwnThread.CallError( const Content : string ); procedure SyncError; begin if Assigned( FInstError ) then FInstError( Content ); end; begin Synchronize( SyncError ); end; procedure TMyOwnThread.Execute; begin ... // Error 1 CallError( 'Fehler 1' ); ... // Error 2 CallError( 'Fehler 2' ); ... // Error 3 CallError( 'Fehler 3' ); usw. end; procedure Form1.InstError(const Content: String); begin RichEditError.Lines.Add(Content); end; |
AW: Synchronize mit Argumenten
Sir Rufo, das ist super!
Ich habe nie daran gedacht, das mit 'Nested'-Methoden zu machen, falls das so heißt. Habe immer 2 direkt in die Klasse gebaut und das fand ich auch immer sehr unschön. Dankeschön :) |
AW: Synchronize mit Argumenten
Müsste die Deklaration von SyncError dann aber nicht aus dem private-Abschnitt verschwinden?
|
AW: Synchronize mit Argumenten
Zitat:
![]() Ich würde aber so gut wie immer statt
Delphi-Quellcode:
den
Synchronize
Delphi-Quellcode:
verwenden (manchmal muss halt Sync sein, aber beim reinen Abliefern von Informationen ...).
Queue
Nur ganz zum Schluss mindestens einmal noch
Delphi-Quellcode:
, damit auch wirklich alles übertragen wird.
Synchronize
Der Vorteil bei
Delphi-Quellcode:
ist ja, dass zwar synchronisiert wird, aber ohne den laufenden Prozess zu unterbrechen. Bei
Queue
Delphi-Quellcode:
wartet beide Threads aufeinander, bis diese synchron laufen, verarbeiten die Informationen und dann geht es für beide erst wieder weiter.
Synchronize
Und warum soll de Thread darauf warten, bis da was auch immer auf den Bildschirm geklatscht werden kann. Das Fenster ist doch schon groß und kann das ganz alleine :mrgreen: Zitat:
|
AW: Synchronize mit Argumenten
Zitat:
Nochmal Danke und LG |
AW: Synchronize mit Argumenten
|
AW: Synchronize mit Argumenten
Verflixte Technik ... geht doch nicht, da "Symbol SyncError kann nicht erfasst werden"
Na dann am besten auf XE umsteigen :mrgreen: Zitat:
|
AW: Synchronize mit Argumenten
Erstmal vielen Dank an alle für die Antworten.
Ich habe den Unterschied zwischen Syncronize und Queue nicht so wirklich verstanden. Und die Hilfe hilft mir beim Verständnis auch nicht wirklich weiter. Kann mir das jemand mal erläutern wann ich Queue verwenden kann und wann ich es nicht darf? |
AW: Synchronize mit Argumenten
Soweit ich es selbst verstanden habe, verhält es sich ähnlich wie bei SendMessage(Synchronize) und PostMessage(Queue): bei Synchronize wartet der Thread, bis der HauptThread die angegebene Methode abgearbeitet hat, bei Queue wird dem HauptThread einfach die Methode "aufs Auge gedrückt", und der Thread läuft weiter. Somit kann bei Verwendung von Queue die Gefahr bestehen, dass der HauptThread nur noch damit beschäftigt ist, die aufgelaufenen Methodenaufrufe abzuarbeiten, nämlich wenn er länger für die Abarbeitung braucht als der Thread. Aber das sind jetzt keine gesicherten Angaben, sondern nur meine Interpretation. Sollte ich mich irren, bitte ich einen Wissenden um Korrektur.
|
AW: Synchronize mit Argumenten
@DeddyH: genau so ist es und genau so steht es in der Hilfe
Zitat:
Zitat:
|
AW: Synchronize mit Argumenten
Hmm... Okay. Wenn das denn nun wirklich so ist :-) Wie ist dass dann bei dem Lösungsansatz von DeddyH und Bummi:
Delphi-Quellcode:
Wenn Error 1 und Error 2 schnell hintereinander ausgeführt werden und der Hauptthread langsam ist, dann würde FError ja evtl. schon verändert worden sein bevor der Hauptthread FError unter Error 1 ausgelesen hat!?!
procedure TMyOwnThread.SyncError;
begin if Assigned(FInstError) then FInstError(FError); end; procedure TMyOwnThread.Execute; begin ... // Error 1 FError := 'Error 1' ; Queue(SyncError); ... // Error 2 FError := 'Error 2' ; Queue(SyncError); usw. end; Also das typische Problem bei zwei Threads und einer Resource. Also könnte man Critical Sections verwenden. Aber dann wäre man wieder blockieren :drunken: Da beißt sich die Schlange wieder selbst in den Schwanz :-D |
AW: Synchronize mit Argumenten
Ob FError in der Zwischenzeit den Wert geändert hat, dürfte keine Rolle spielen, da die Methode ja bereits mit dem entsprechenden Parameter in die Abarbeitungsschleife des MainThreads gestellt wurde.
|
AW: Synchronize mit Argumenten
Leider funktioniert es nicht.
Habe mal folgenden Versuch gemacht:
Delphi-Quellcode:
type
TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); private FMyThread: TMyThread; procedure progress(value: Integer); public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Clear; FMyThread := TMyThread.Create(true); FMyThread.Progress := progress; FMyThread.Resume; end; procedure TForm1.progress(value: Integer); begin Memo1.Lines.Add('Value: ' + IntToStr(value)); sleep(500); end;
Delphi-Quellcode:
Die Ausgabe sieht so aus:
type
TProgress = procedure(Value: integer) of object; type TMyThread = class(TThread) private FValue: Integer; FProgress: TProgress; procedure SyncProgress; protected procedure Execute; override; public property Progress: TProgress write FProgress; end; implementation uses Windows, SysUtils; { TMyThread } procedure TMyThread.Execute; var i: Integer; begin for i := 1 to 10 do begin FValue := i; Queue(SyncProgress); sleep(200); OutputDebugString(PWideChar('FValue: ' + IntToStr(FValue))); end; end; procedure TMyThread.SyncProgress; begin FProgress(FValue); end; Zitat:
|
AW: Synchronize mit Argumenten
Und bei Synchronize stimmt das Ergebnis?
|
AW: Synchronize mit Argumenten
Na logisch stimmt das bei Synchronize.
In die Warteschlange kommt ja der Aufruf der Prozedur. beim Abarbeiten wird die Prozedur ausgeführt. Und bei der Verwendung von Queue (asynchron) hat sich der Wert von FValue eben schon wieder geändert. Bei Synchronize schweigt ja alles, bis die Prozedur abgearbeitet wurde. Für das Queue hilft also auch nur einen WerteQueue (mit CS absichern) die dann Stück für Stück abgearbeitet wird. Nimm für die WerteQueue eine eigene CS, damit die auch nur dann blockiert ist, wenn dort Werte abgerufen oder eingetragen werden. |
AW: Synchronize mit Argumenten
@Sir Rufo
Der Zugriff über CS ist sicher sinnvoll, ändert aber nichts daran dass man sich im klaren sein muss wo der Unterschied zwischen Synchronize und Queue liegt und welchen Tod man sterben möchte. Es wird Anwendungen geben bei es beispielsweise genügt wenn der Hauptthread einen gerade aktuellen Wert zu Anzeige bringt, hier wird Queue der richtige Ansatz sein. Wenn sichergestellt werden muss dass alle Messwerte auflaufen wird es auf Synchonize oder auf z.B. gemeinsam genutzte Array hinauslaufen, bei denen der Thread eine CS gesicherte "gültig bis" Property verwaltet, oder bei einfachen Datentypen direkt Messages. |
AW: Synchronize mit Argumenten
@Bummi
Wenn man mit Queue arbeitet MUSS man den Zugriff auf die entsprechende Ressource (hier FValue) schützen. Andernfalls riskiert man einen Zugriffsfehler. Bei Synchronize kann man sich das sparen, da zum Zeitpunkt der Übergabe Thread und Hauptthread im gleichen Threadkontext laufen. Bei Queue eben halt nicht. Mit einer MessageQueue werden via Queue auch ALLE Nachrichten abgeliefert. Mit nur einer Resoource (hier FValue) natürlich nur der aktuelle Wert zum Zeitpunkt der Abfrage. Hat man sich also entschlossen mit Queue zu arbeiten (weil man eben nur Daten in den Hauptthread schleusen will und von dem auch nichts direkt zurückhaben will und genau da macht es durchaus Sinn), dann ist eine kleine Liste mit den entsprechenden Nachrichten doch auch kein Problem mehr. Nur sollte man dabei genau wissen, was man tut, sonst knallt es völlig unkontrolliert (und perverserweise auch nicht regelmäßig und je nach Häufigkeit des Zugriffs auch nicht wirklich oft ... aber es knallt halt :) |
AW: Synchronize mit Argumenten
@Sir Rufo
ich war schon vorher Deiner Meinung, meine Thread-Templates sind auch so aufgebaut. Mir ging es nur darum festzustellen dass es wohl jedes mal abzuwägen gilt welche der Varianten (Queue/Synchronize) für den Anwendungsfall gerade sinnvoll ist. Zitat:
BTW leicht offtopic, wie handhabst Du Syncs zwischen Threads und SubThreads (oder nebenläufigen Threads), also ohne Umweg über den Anwendungsthread? |
AW: Synchronize mit Argumenten
Wieso gehen beim Queue Werte verloren? Klar, wenn man es falsch macht ja, aber wenn ich das Auto nicht richtig beherrsche fahre ich evtl. auch gegen den Baum. :mrgreen:
Also am besten richtig machen ;) Bei meinen Queues gehen keine Werte verloren. (und seit der anonymen Methoden geht halt auch das Queue ohne eigene Warteschlange) Wichtig ist nur am Ende des Threads ein Synchronize zu machen, damit auch wirklich alle Queue-Einträge im Hauptthread verarbeitet wurden. Ansonsten kann es eben halt passieren, dass der Thread (FreeOnTerminate) sich einfach so auflöst und alle bis dahin nicht abgearbeitete Queue-Einträge aus selbigen Thread gehen sang und klanglos mit unter. Zwischen den Threads (auch Hauptthread direkt zum Thread) geht das bei mir immer über eine CS. Manchmal halt auch eine separate CS für den reinen Austausch dieser Daten, wenn es vom Ablauf geschickter ist und dadurch performanter wird. ADD Daten von Thread zu Thread über den Hauptthread auszutauschen halte ich nicht für sinnvoll. Der Hauptthread ist für den User und wird von dem quasi kontrolliert. Wenn der also Samba auf der Tastatur und Maus tanzt, dann könnte er damit auch die Threads beeinflussen. Bei mir darf der das (wenn es nicht meine Maus/Tastatur ist) ;) |
AW: Synchronize mit Argumenten
Wenn man zusammen mit dem Prozedurzeiger auch noch die Parameter mit in dem Queue ablegt, dann gäbt es diesbezüglich auch keine Probleme.
Bzw., man eh nur die selbe Funktion aufrugt, dann reichen auch nur die Werte im Queue, wenn nichts verlorengehen soll. |
AW: Synchronize mit Argumenten
Zitat:
|
AW: Synchronize mit Argumenten
Ich steh immer noch auf dem Schlauch. Habe den Zugriff mit eine CS geschützt.
In dieser CS übertrage ich meine Daten an eine TQueue (Das erste mal dass ich mit Generics gearbeitet habe :stupid:) damit die Blockierung auf ein zeitliches Minimum begrenzt bleibt. Allerdings blockiert das Abarbeiten der Queue die Funktion und es gehen immer noch Werte 'verloren'.
Delphi-Quellcode:
type
TValueQueue = TQueue<Integer>; TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); private FMyThread: TMyThread; FValueQueue: TValueQueue; procedure progress(value: Integer); public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Clear; FValueQueue := TValueQueue.Create; FMyThread := TMyThread.Create(true); FMyThread.Progress := progress; FMyThread.Resume; end; procedure TForm1.progress(value: Integer); begin FMyThread.SyncCS.Enter; FValueQueue.Enqueue(value); FMyThread.SyncCS.Leave; while FValueQueue.Count > 0 do begin Memo1.Lines.Add('Value: ' + IntToStr(FValueQueue.Dequeue)); sleep(500); end; end;
Delphi-Quellcode:
Wie mache ich es richtig(er)?
type
TProgress = procedure(Value: integer) of object; type TMyThread = class(TThread) private FValue: Integer; FProgress: TProgress; FSyncCS: TCriticalSection; procedure SyncProgress; protected procedure Execute; override; public property Progress: TProgress write FProgress; property SyncCS: TCriticalSection read FSyncCS; end; implementation uses Windows, SysUtils; { TMyThread } procedure TMyThread.Execute; var i: Integer; begin FSyncCS := TCriticalSection.Create; for i := 1 to 10 do begin FSyncCS.Enter; FValue := i; FSyncCS.Leave; Queue(SyncProgress); sleep(200); end; //if assigned(FSyncCS) then // FreeAndNil(FSyncCS); end; procedure TMyThread.SyncProgress; begin FProgress(FValue); end; |
AW: Synchronize mit Argumenten
!!! OFF TOPIC !!!
Ist ja echt frustrierend :evil: RAD heißt doch Rapid Application Development. Und es heißt doch immer mit Delphi geht alles immer so einfach im Gegensatz zu cpp. Ich habe ein Embedded Projekt mit Qt geschrieben und dachte mir ich versuche jetzt mal ob ich bei Qt auch dieses Problem habe.... Und siehe da, alles ganz einfach. Keine zusätzlichen privaten Felder in denen die zu übergebenden Werte stehen, keine zusätzlichen CS, keine zusätzlichen Methoden, keine Generic Queue oder was weiß ich für Tricksereien und im Gegensatz zu meiner Lösung bei Delphi funktioniert diese Qt Lösung (auf Anhieb nach 10 min) :-(
Code:
class MainWindow : public QMainWindow
{ Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_pushButton_clicked(); void onProgress(qint32); private: Ui::MainWindow *ui; }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_clicked() { MyThread *myThread; myThread = new MyThread(this); connect(myThread, SIGNAL(progress(qint32)), this, SLOT(onProgress(qint32))); myThread->start(); } void MainWindow::onProgress(qint32 value) { ui->plainTextEdit->appendHtml("Value: " + QString::number(value)); Sleep(500); qDebug() << QThread::currentThreadId() << "MainWindow::onProgress - Value:" << QString::number(value); }
Code:
class MyThread : public QThread
{ Q_OBJECT public: explicit MyThread(QObject *parent = 0); signals: void progress(qint32 value); public slots: protected: void run(); }; #include "MyThread.h" #include <windows.h> #include <QDebug> MyThread::MyThread(QObject *parent) : QThread(parent) { } void MyThread::run() { for(qint32 i = 1; i <= 10; i++) { emit progress(i); Sleep(200); qDebug() << QThread::currentThreadId() << "MyThread::run() - Value:" << QString::number(i); } qDebug() << QThread::currentThreadId() << "Fertig"; } Zitat:
Und wie mache ich es jetzt richtig in Delphi (siehe #25)? |
AW: Synchronize mit Argumenten
Du übergibst da aber den Wert direkt an den Thread, bzw du gibst jedem Thread eine Kopie, so daß alle Threads ihre eigenen Werte haben.
In diesen Delphibeispielen nutzt du eine externe Variable für alle Threads. > Äpfel und Birnen |
AW: Synchronize mit Argumenten
@himitsu
und wie übergebe ich Delphi eine Kopie per Queue? |
AW: Synchronize mit Argumenten
Sir Rufo hat ja bereits auf den
![]() Allerdings muss man dann aufpassen (habe ich am Ende des Artikels auch drauf hingewiesen), daß der Thread sich nicht bereits beendet bevor die Queues alle abgearbeitet wurden. Ansonsten gehen die letzten Einträge nämlich verloren (gilt aber für alle Implementationen, die Queue benutzen). Der Vorteil der Anonymen Methode ist einfach, daß man sich um die Kapselung des Parameters keine Gedanken mehr machen muss - das erledigt der Compiler. Das ganze Brimborium mit der TValueQueue ist damit völlig überflüssig. Ach ja: Sollte jemand Probleme haben, den Artikel zu vestehen, da er auf Englisch verfasst ist, könnte ich mich vielleicht dazu durchringen, eine Deutsche Fassung hier an geeigneter Stelle (wo?) bereitzustellen. |
AW: Synchronize mit Argumenten
Also ich bekomme es mit queue nicht hin. :(
Wie muss mein Code von #16 oder #26 abgeändert werden damit das funktioniert? Könnte den korrekten code jemand evtl. posten? Wäre sehr dankbar! |
AW: Synchronize mit Argumenten
Danke Uwe, ich hoffe ich habe es richtig verstanden:
Delphi-Quellcode:
Korrekturvorschläge willkommen....
unit Unit2;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TProgress = procedure(const Value: integer) of object; type TMyThread = class(TThread) private FValue: Integer; FProgress: TProgress; procedure SyncProgress(Value:Integer); protected procedure Execute; override; public property Progress: TProgress write FProgress; end; TForm2 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private-Deklarationen } FMyThread: TMyThread; procedure progress(const value: Integer); public { Public-Deklarationen } end; var Form2: TForm2; implementation {$R *.dfm} procedure TMyThread.Execute; var i: Integer; begin for i := 1 to 10 do begin FValue := i; SyncProgress(i); sleep(200); OutputDebugString(PWideChar('FValue: ' + IntToStr(FValue))); end; end; procedure TMyThread.SyncProgress(Value:Integer); begin Queue( Procedure begin FProgress(Value); end ); end; procedure TForm2.Button1Click(Sender: TObject); begin Memo1.Clear; FMyThread := TMyThread.Create(true); FMyThread.Progress := progress; FMyThread.Resume; end; procedure TForm2.progress(const value: Integer); begin Memo1.Lines.Add('Value: ' + IntToStr(value)); sleep(500); end; end. |
AW: Synchronize mit Argumenten
Also auch mit den Änderungen sieht die Ausgabe bei mir so aus:
Zitat:
|
AW: Synchronize mit Argumenten
bei mir so, XE Pro
Code:
mhhhhhhh.....:gruebel:
Value: 1
Value: 2 Value: 3 Value: 4 Value: 5 Value: 6 Value: 7 Value: 8 Value: 9 Value: 10 vielleicht können Uwe oder Sir Rufo uns nochmals auf die Sprünge helfen. |
AW: Synchronize mit Argumenten
Der Code von Bummi in #31 ist soweit in Ordnung, bis auf ein paar Kleinigkeiten:
a) das Feld FValue kann entfallen, da es nur noch innerhalb Execute verwendet wird b) du musst noch etwas tun, damit der Thread nicht beendet wird, bevor die Queues abgearbeitet sind. Das genaue Verhalten ist natürlich von diversen Umständen abhängig, aber bei meinem System fehlt die 10 im Memo, wenn ich das OutputDebugString auskommentiere und es kommt fast gar nichts mehr an, wenn ich das Sleep(200) auch weglasse. Und Viktorii: Wenn es bei dir nicht geht, hast du vermutlich noch einen Fehler drin. Kann ich ohne aktuelle Sourcen aber nicht erkennen. |
AW: Synchronize mit Argumenten
Zitat:
Seit wann kann Delphi 5 mit Generics umgehen? |
AW: Synchronize mit Argumenten
Zitat:
|
AW: Synchronize mit Argumenten
Also wie das Delphi 5 dahinkommt kann ich mir auch nicht erklären :shock:
Hab jetzt mittlerweile auch Delphi XE. Sorry für die Verwirrung. Was den Code angeht: Kann sein dass ich da einen Bug eingebaut habe. War ein wenig zwischen Tür und Angel getestet. Wie es aussieht habe ich vor Montag kein Delphi zur Verfügung. Werde es spätestens dann testen. Schönes Wochenende an alle, |
AW: Synchronize mit Argumenten
nochmals Danke an Uwe und Sir Rufo
|
AW: Synchronize mit Argumenten
Liste der Anhänge anzeigen (Anzahl: 2)
Sodele, jetzt habe ich mal einen DemoThread inkl. einer Exe zum Ausprobieren.
Mit der Exe kann man schön das Zeitverhalten von Queue und Sync beobachten (auch, dass keine Werte verloren gehen). Für den Vergleich unter Last gibt es unten den Schalter "Arbeitstimer aktiv". Dieser simuliert eine starke Auslastung vom MainThread. Interessant ist dabei das Verhalten von Queue und Sync zu sehen. Den Code-Vorschlag von Uwe habe ich mal eingearbeitet (als SlowSync) und eine kleine Abwandlung (FastSync). Das Fast und SLow kommt allerdings erst dann zum Tragen, wenn das Event für den Sync nicht zugewiesen ist, also eigentlich kein Sync erfolgen müsste. Der Code von Uwe führt aber immer den Sync aus => SlowSync Mein Code führt den Sync nur aus wenn auch wirklich benötigt => FastSync Aber ... der Zugriff auf die Prop OnInfoEvent erfolgt beim FastSync noch im ThreadKontext und muss daher entsprechend geschützt sein (siehe Getter von OnInfoEvent). Der SlowSync kann direkt auf des Feld FOnInfoEvent zugreifen (es sei denn diese Prop würde auch von anderen Thread gelesen/beschrieben, dann muss das eh komplett mit einer CS gesichert werden) Hier der ThreadCode (Exe und kompletter Source im Anhang):
Delphi-Quellcode:
unit uDemoThread;
interface uses Classes, SyncObjs; type TNotifyInfoEvent = procedure( const Info : string; Ticks : Cardinal ) of object; TThreadExecType = ( tetSlowSync, tetFastSync, tetQueue ); TDemoThread = class( TThread ) private { Private-Deklarationen } FEventCS : TCriticalSection; FOnInfoEvent : TNotifyInfoEvent; FExecType : TThreadExecType; procedure CallSlowSyncEvent( const Info : string; Ticks : Cardinal = 0 ); procedure CallFastSyncEvent( const Info : string; Ticks : Cardinal = 0 ); procedure CallQueueEvent( const Info : string; Ticks : Cardinal = 0 ); function GetOnInfoEvent : TNotifyInfoEvent; procedure SetOnInfoEvent( const Value : TNotifyInfoEvent ); protected procedure Execute; override; public constructor Create( CreateSuspended : Boolean; ExecType : TThreadExecType = tetSlowSync ); destructor Destroy; override; published property OnInfoEvent : TNotifyInfoEvent read GetOnInfoEvent write SetOnInfoEvent; end; implementation uses SysUtils, Windows; { TDemoThread } procedure TDemoThread.CallSlowSyncEvent( const Info : string; Ticks : Cardinal ); var IntTicks : Cardinal; begin // Der SlowSync synchronisiert IMMER mit dem Hauptthread // egal, ob ein Event verknüpft ist oder nicht // Wenn kein Event verknüpft ist, bräuchte ja eigentlich kein Sync erfolgen if MainThreadID = GetCurrentThreadId then begin if Assigned( FOnInfoEvent ) then FOnInfoEvent( Info, Ticks ); end else begin IntTicks := GetTickCount; Synchronize( procedure begin CallSlowSyncEvent( 'via SlowSync: ' + Info, IntTicks ) end ); end; end; procedure TDemoThread.CallFastSyncEvent( const Info : string; Ticks : Cardinal ); var IntTicks : Cardinal; begin // Der Fast-Sync synchronisiert nur, wenn auch ein Event zugewiesen wurde // Da diese Abfrage aber noch im Thread-Kontext erfolgt, muss die // Eigenschaft OnInfoEvent über eine CS abgesichert werden if Assigned( OnInfoEvent ) then if MainThreadID = GetCurrentThreadId then FOnInfoEvent( Info, Ticks ) else begin IntTicks := GetTickCount; Synchronize( procedure begin CallFastSyncEvent( 'via FastSync: ' + Info, IntTicks ) end ); end; end; procedure TDemoThread.CallQueueEvent( const Info : string; Ticks : Cardinal ); var IntTicks : Cardinal; begin // Beim Queue ist es völlig wurscht, ob da nun eine Sync erfolgt oder nicht, // denn dieser Aufruf erfolgt eh asynchron und beschäftigt den Thread nicht weiter if MainThreadID = GetCurrentThreadId then begin if Assigned( OnInfoEvent ) then FOnInfoEvent( Info, Ticks ); end else begin IntTicks := GetTickCount; Queue( procedure begin CallQueueEvent( 'via Queue: ' + Info, IntTicks ) end ); end; end; constructor TDemoThread.Create( CreateSuspended : Boolean; ExecType : TThreadExecType ); begin FEventCS := TCriticalSection.Create; FEventCS.Enter; try inherited Create( CreateSuspended ); FExecType := ExecType; FreeOnTerminate := True; finally FEventCS.Leave; end; end; destructor TDemoThread.Destroy; begin FEventCS.Enter; try inherited; finally FEventCS.Leave; FreeAndNil( FEventCS ); end; end; procedure TDemoThread.Execute; var idx : Integer; Info : string; begin Sleep( 25 ); { Thread-Code hier einfügen } for idx := 0 to 10 do begin Info := 'Ich bin bei ' + IntToStr( idx ); case FExecType of tetSlowSync : CallSlowSyncEvent( Info ); tetFastSync : CallFastSyncEvent( Info ); tetQueue : CallQueueEvent( Info ); end; end; // Wenn dieser "Slow"-Sync nicht gemacht wird, kann es sein, // dass einige Nachrichten über die Queue verloren gehen!!!! case FExecType of tetSlowSync: CallSlowSyncEvent( 'Fettich!' ); tetFastSync: CallFastSyncEvent( 'Fettich!' ); tetQueue: CallSlowSyncEvent( 'Fettich! (Queue)' ); end; end; function TDemoThread.GetOnInfoEvent : TNotifyInfoEvent; begin FEventCS.Enter; try Result := FOnInfoEvent; finally FEventCS.Leave; end; end; procedure TDemoThread.SetOnInfoEvent( const Value : TNotifyInfoEvent ); begin FEventCS.Enter; try FOnInfoEvent := Value; finally FEventCS.Leave; end; end; end. |
AW: Synchronize mit Argumenten
So, der Code funktioniert :bounce1:
Jetzt muss ich nur noch das Beispiel von Sir Rufo komplett nachvollziehen :) Vielen Dank an Alle :thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:19 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