AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Delphi Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.
Thema durchsuchen
Ansicht
Themen-Optionen

Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.

Ein Thema von HintByError · begonnen am 6. Jun 2022 · letzter Beitrag vom 7. Jun 2022
Antwort Antwort
HintByError

Registriert seit: 13. Dez 2018
Ort: Marburg
20 Beiträge
 
Delphi XE2 Professional
 
#1

Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.

  Alt 6. Jun 2022, 21:42
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

  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:
  downloadInProgress := downloadItem.IsInProgress and
    (downloadItem.FullPath <> '');
Die Feldvariable „downloadInProgress“ ist false, wenn der Download abgeschlossen ist. Diese Feldvariable wird auch von „OnCanClose“ des Hauptformulars verwendet.

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:
  TThreadTimer = class helper for TTimer
    procedure enterNotification;
    procedure runNotification(notification:TNotifyEvent);
    procedure exitNotification;
    class procedure notificationFinished(Sender:TObject);
  end;
Implementation:
Delphi-Quellcode:
  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;
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“:
Delphi-Quellcode:
  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;
Der Code von „showPnlDownloadFeature“, der auf dem Hauptthread ausgeführt wird, sieht dann wie folgt aus:
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;
Wolfgang Sauer
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.157 Beiträge
 
Delphi 12 Athens
 
#2

AW: Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.

  Alt 6. Jun 2022, 21:51
Ist dir nie aufgefallen, dass es mehrere Varianten der Methoden gibt?
Einmal normal und dann nochmal als class-procedure.
Delphi-Referenz durchsuchenTThread.Synchronize
TThread.Synchronize(nil, ....);


Auch wenn es nie die "richtige" TThread-Instanz liefert, selbst wenn es ein Delphi-Thread wäre.
Delphi-Referenz durchsuchenTThread.CurrentThread
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)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu ( 6. Jun 2022 um 21:58 Uhr)
  Mit Zitat antworten Zitat
HintByError

Registriert seit: 13. Dez 2018
Ort: Marburg
20 Beiträge
 
Delphi XE2 Professional
 
#3

AW: Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.

  Alt 6. Jun 2022, 22:24
Das Eigenschaftfeld TThread.CurrentThread ist mir durch Deinen Hinweis das erstmals aufgefallen.
Danke dafür. Ich werde es damit auch mal ausprobieren.
Wolfgang Sauer
  Mit Zitat antworten Zitat
HintByError

Registriert seit: 13. Dez 2018
Ort: Marburg
20 Beiträge
 
Delphi XE2 Professional
 
#4

AW: Das Chrome-Embedded-Framework und die Synchronisierung ohne TThread.Symchronize.

  Alt 7. Jun 2022, 11:32
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:
  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;
Anweisungen in „showPnlDownloadFeature“:
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;
Wolfgang Sauer
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:55 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