Einzelnen Beitrag anzeigen

HintByError

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

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

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