![]() |
Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.
Die Klasse „TChromium“ bietet das Setzen Methodenzeiger für eine Reihe von Ereignissen an. Die auf diesem Weg eingebundenen Methoden werden nicht auf dem Hauptthread ausgeführt, sondern auf einem nebenläufigen Thread. Dabei ist „TChromium“ nicht von „TThread“ abgeleitet und es bietet sich nicht die Synchronisierung auf den Hauptthread mittels „TThread.Synchronize“ an. Lange Rede kurzer Sinn, ich habe eine funktionierende Lösung mittels einer Erweiterung von „TTimer“ durch eine Hilfsklasse. Ich stelle die Lösung hier in Skizzenform zur Diskussion vor. Es kann sein, dass es noch eine einfachere Lösung gibt, deren ich mir nicht bewusst bin. Suchanfragen mittels globaler Suchmaschinen haben aber gezeigt, dass das Problem nicht generell befriedigend behandelt wird.
Synchronisiert werden soll der Methodenzeiger „OnDownLoadUpdated“ einer „TChromium“-Instanz. Wenn die zugeordnete Methode aufgerufen wird, dann ist im Anweisungsblock die Bedingung
Delphi-Quellcode:
GetCurrentThreadId <> MainThreadId
erfüllt. So kann man programmiert nachfragen, ob eine Routine in einem nebenläufigen Thread ausgeführt wird. Man kann auch in Ansicht->Debug-Fenster->Threads nachsehen. Der erste gelistete Thread ist der Hauptthread und der aktive ist markiert. Innerhalb von „OnDownLoadUpdated“ lässt sich abfragen, ob der Download abgeschlossen worden ist:
Delphi-Quellcode:
Die Feldvariable „downloadInProgress“ ist false, wenn der Download abgeschlossen ist. Diese Feldvariable wird auch von „OnCanClose“ des Hauptformulars verwendet.
downloadInProgress := downloadItem.IsInProgress and
(downloadItem.FullPath <> ''); Wenn der Download abgeschlossen ist, dann soll ein Panel mit einer Schaltfläche zum Anzeigen der Datei über „ShellExecute“ sichtbar gemacht werden und dabei der Dateiname auf der Schaltfläche angezeigt werden. Dies muss auf dem Hauptthread erfolgen. Damit der entsprechende Code auf dem Hauptthread ausgeführt werden kann, wird ein „Timer“-Objekt auf unkonventionelle Weise verwendet. Es ist so, dass die dem Feld „OnTimer“ zugeordnete Methode garantiert auf dem Hauptthread ausgeführt wird. Diese kann dann selbst den Timer über die Eigenschaft „Enabled“ ausschalten, sodass die Methode „OnTimer“ exakt einmal und nicht öfter ausgeführt wird. Ferner ist eine Objektsperre mittels „System.TMonitor.Enter“ notwendig, um entsprechende Felder aus dem Hauptformular, dem die „OnTimer“-Methode zugeordnet ist, für die Parameterübergabe an die „OnTimer“-Methode zu reservieren. Erweiterung der Klasse „TTimer“ mittels der Hilfsklasse „TThreadTimer“:
Delphi-Quellcode:
Implementation:
TThreadTimer = class helper for TTimer
procedure enterNotification; procedure runNotification(notification:TNotifyEvent); procedure exitNotification; class procedure notificationFinished(Sender:TObject); end;
Delphi-Quellcode:
Die verwendete „TTimer“-Instanz heißt „tmrResize“. Im Designer ist „Enabled“ auf false gesetzt und das „OnTimer“-Ereignis bleibt dort undefiniert. Es wird dann per Code gesetzt. Der Synchronisierungscode in der nebenläufigen Methode des Ereignisses „OnDownLoadUpdated“:
procedure TThreadTimer.enterNotification;
begin System.TMonitor.Enter(self); //Objektsperre. repeat until Enabled=false; //warten auf nicht abgeschlossene „OnTimer“-Methode end; procedure TThreadTimer.runNotification(notification:TNotifyEvent); begin OnTimer := notification; //Methodenzeiger setzen Enabled := true; //Methodenaufruf per Timer zulassen end; procedure TThreadTimer.exitNotification; begin System.TMonitor.Exit(self); //Objektsperre aufheben end; class procedure TThreadTimer.notificationFinished(Sender:TObject); begin TTimer(Sender).Enabled := false; //„OnTimer“ hat Ausführung abgeschlossen und hebt Sperre auf end;
Delphi-Quellcode:
Der Code von „showPnlDownloadFeature“, der auf dem Hauptthread ausgeführt wird, sieht dann wie folgt aus:
if not downloadInProgress and (downloadItem.FullPath <> '') then begin
//Download ist abgeschlossen und eine Zieldatei ist festgelegt try tmrResize.enterNotification; //Reservierung der „TTimer“-Instanz „tmrResize“ und des ... //Zugriffs auf die Feldvariablen für die Parameterübergabe. latestDownload := copy(downloadItem.FullPath, 1, Length(downloadItem.FullPath)); //Parameter für das „OnTimer“-Ereignis setzen tmrResize.runNotification(showPnlDownloadFeature); //„showPnlDownloadFeature“ als „OnTimer“-Ereignis verwenden. finally tmrResize.exitNotification; //Freigabe des TTimerobjektes. Die Freigabe der Sperre mittels //„Enabled“ in der „TTimer“-Instanz erfolgt dann im „OnTimer“- //Ereignis end; end;
Delphi-Quellcode:
try
pnlDownloadFeature.Visible := true; //Panel mit Option Dateiladen sichtbar machen btnDownloadFeature.Caption := ExtractFileName(latestDownload) + ' laden'; //Dateinamen als Schaltfeldbeschriftung verwenden. Application.ProcessMessages; //Bildschirm aktualsieren Chrome.SetFocus(true); //Fokus auf Browser belassen finally TTimer.notificationFinished(Sender); //abschließend wird die „TTimer“-Instanz deaktiviert und mittels //„Enabled“=false die Freigabe signalisiert. Diese Methode wird //mit diesen hier beschriebenen Zugriffen exakt einmal aufgerufen. end; |
AW: Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.
Ist dir nie aufgefallen, dass es mehrere Varianten der Methoden gibt?
Einmal normal und dann nochmal als class-procedure. :roll: ![]()
Delphi-Quellcode:
TThread.Synchronize(nil, ....);
Auch wenn es nie die "richtige" TThread-Instanz liefert, selbst wenn es ein Delphi-Thread wäre. ![]()
Delphi-Quellcode:
TThread.CurrentThread.Synchronize(...);
Wobei das CurrentThread immer ein TExternalThread ist und dort nicht alles funktioniert, wie z.B. Terminate, da Terminate nur eine Variable setzt, was hier nichts bringt, weil es ja eine andere Instanz ist. PS: GetCurrentThreadId vs. TThread.CurrentThread.ThreadId (Letzteres für Multiplatform-Code, aber im Windows würde ich eher Ersteres verwenden) |
AW: Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.
Das Eigenschaftfeld TThread.CurrentThread ist mir durch Deinen Hinweis das erstmals aufgefallen.
Danke dafür. Ich werde es damit auch mal ausprobieren. |
AW: Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.
Man bekommt die Sache mit „TThread.Synchronize(nil,showPnlDownloadFeature)“ zum laufen. Allerdings mit einer Einschränkung: der Aufruf von „Application.ProcessMessages;“ ist dann in „showPnlDownloadFeature“ nicht möglich. Die auf „TTimer“ basierende Lösung erlaubt also mehr Zugriffe.
Prinzipiell ist eine Parameterübergabe von lokalen Variablen der nebenläufigen Thread-Prozedur an den Haupt-Thread nicht möglich, sondern man muss globale Variablen auf dem Heap oder im Datensegment verwenden. Damit verschiedene Threads, die die gleiche Methode als Ereignis zur Folge haben, bei dem Zugriff auf die Parameterübergabe an den Haupt-Thread nicht kollidieren, ist eine Zugriffssperre mittels „Interlocked.Increment“ notwendig. Bei der „TTimer“-Variante ist dies mit mittels der Eigenschaft „Enabled“ der „TTimer“-Instanz gelöst. Code für die Auslösung der Synchronisierung:
Delphi-Quellcode:
Anweisungen in „showPnlDownloadFeature“:
if not downloadInProgress and (downloadItem.FullPath <> '') then begin
try repeat until (TInterLocked.Increment(lockcount)=1); //Zugriffsmöglichkeit abwartem und sperren latestDownload := copy(downloadItem.FullPath, 1, Length(downloadItem.FullPath)); //Parameterübergabe mit globaler Variable an „showPnlDownloadFeature“ except //Fehlerbehandlung: TInterLocked.Decrement(lockcount); raise; end; TThread.Synchronize(nil,showPnlDownloadFeature); //Synchronisieren mit dem Haupt-Thread, wenn kein Fehler. end;
Delphi-Quellcode:
try
pnlDownloadFeature.Visible := true; btnDownloadFeature.Caption := ExtractFileName(latestDownload) + ' laden'; //Visualisierung und Schaltfläche für Download zugänglich machen // Application.ProcessMessages;//bei Synchronize nicht erlaubt Chrome.SetFocus(true); finally TInterLocked.Decrement(lockcount); //Sperre aufheben, da Parameter ab hier nicht mehr ausgewertet werden. end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:29 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