2017-02-09 3 views
1

Ich weiß, dass Delphi-Threads auf vielen Threads diskutiert wurden. Ich habe versucht, sie zu überprüfen, fand aber keine Antwort auf meine Frage.Ist dieser Delphi Thread Code korrekt?

Hintergrund: Ich habe festgestellt, dass eine TWebBrowser befreien nehmen 10+ Sekunden, nachdem der Browser Adobe Acrobat Reader DC geladen. Ich denke irgendwie nach Updates oder etwas zu suchen. Es ist ärgerlich, wenn man versucht, ein Formular mit einem Browser zu schließen.

Ich dachte vielleicht könnte ich einen Hintergrund-Thread den Browser freigeben lassen. Also habe ich die Browser-Variable in eine globale Variable verschoben (privat im Implementierungsteil der Unit gespeichert). Nur eine dieser Formen würde gleichzeitig verwendet werden. Dann habe ich versucht, einen Thread frei im Hintergrund zu haben. Es funktioniert nicht so, wie ich es erwartet hätte.

Beispiel Code

interface 
    TMyform = class(TForm) 
    pnlBowserHolder: TPanel; 
    procedure FormDestroy(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    private 
    //WebBrowser : TWebBrowser; <-- moved to global variable 
    public 
    { Public declarations } 
    end; 

implementation 

type 
    TBackgroundBrowserKillerThread = class(TThread) 
    public 
    procedure Execute; override; 
    end; 

var 
    WebBrowser : TWebBrowser; 
    BrowserKillerThread : TBackgroundBrowserKillerThread; 

procedure TfrmLabImageViewer.FormCreate(Sender: TObject); 
begin 
    WebBrowser := TWebBrowser.Create(Self); 
    TWinControl(WebBrowser).Parent := pnlBowserHolder; 
    WebBrowser.Align := alClient; 
end; 

procedure TfrmLabImageViewer.FormDestroy(Sender: TObject); 

begin 
    BrowserKillerThread := TBackgroundBrowserKillerThread.Create(true); 
    Application.ProcessMessages; 
    BrowserKillerThread.Execute(); 
    //WebBrowser.Free; 
end; 

procedure TBackgroundBrowserKillerThread.Execute(); 
begin 
    TWinControl(WebBrowser).Parent := nil; 
    FreeAndNil(WebBrowser); 
    self.FreeOnTerminate := true; 
    BrowserKillerThread := nil; //free reference to thread, shouldn't affect ability of self to free itself (?) 
end; 

Fragen:

  • wenn ich durch den Formdestroy Code in der Debug-Modus Schritt wird die Zeile mit BrowserKillerThread.Execute(); dauert noch 10 Sekunden zur Ausführung. Ich hatte gedacht, dass dies den anderen Thread starten und sofort zurückkehren würde. Aber das tut es nicht. Ist mein Verständnis falsch was .execute tut? Oder ist etwas Lustiges los?
  • Ist das was ich mache eine schlechte Sache? Ich habe gelesen, dass die VCL nicht threadsicher ist, und dass man nicht auf VCL-Objekte von dem anderen Thread zugreifen kann/sollte. Ich hatte gehofft, dass dies in diesem Fall nicht gelten würde, da ich das Objekt nur frei lasse, ohne weitere Interaktion geplant zu haben.
  • Wenn ein Thread frei ist nach Beendigung, nehme ich an, dies würde meinen BrowserKillerThread Zeiger baumeln lassen. Ist es also in Ordnung, so wie ich nil zuzuweisen?
  • Irgendwelche Vorschläge, wie man das besser macht?

Vielen Dank im Voraus.

KT

+3

Dies wird nie funktionieren. Sie können von keinem Ort auf ein VCL-Steuerelement irgendeiner Art außer dem Hauptthread zugreifen, und Sie können es nicht in einem Thread erstellen und es in einem anderen Thread zerstören. Der Code, den Sie geschrieben haben, ändert nichts - der Browser wird immer noch von dem Hauptthread zerstört, der ihn besitzt; Sie rufen nur diesen Code von Ihrem Thread (was falsch ist, es sei denn, Sie tun es über Synchronisieren). –

+4

Zusätzlich ... Vielleicht möchten Sie alle diese Threads erneut überprüfen. Ich wette, Sie haben kein einziges Beispiel gesehen, bei dem die execute-Methode von außen aufgerufen wurde. Warum? Denn dann würde der Code in der execute-Methode im Kontext des Threads ausgeführt werden, der ihn aufruft. –

+2

Ich bekomme nicht die Verzögerung beim Schließen der App, die Sie mit TWebBrower + v.15.023 von Acro Reader DC beschreiben. Können Sie eine Beispiel-URL angeben, die die Verzögerung für Sie provoziert? – MartynA

Antwort

8

Ist mein Verständnis falsch von dem, was .execute tut ??

Ja. So tust du nicht Threading. In der Regel müssen Sie sich erst entscheiden, ob Sie etwas mit dem Thread tun müssen, nachdem Sie ihn gestartet haben. Wenn ja, behalten Sie eine Referenz, verwenden Sie nicht FreeOnTerminate, und kümmern Sie sich selbst um die Zerstörung des Threads nach dessen Beendigung. Wenn Sie dies nicht tun, behalten Sie keine Referenz, setzen Sie FreeOnTerminate, und senden Sie es ab.

Sie würden weder FreeOnTerminate innerhalb der Ausführung eines Threads festlegen, noch einen globalen Verweis auf einen Thread mit dieser Gruppe behalten. Sie starten auch keinen Thread, indem Sie die Methode Execute aufrufen, indem Sie sie entweder nicht suspendieren oder indem Sie Start aufrufen. Der Aufruf von Execute startet den Thread eigentlich nicht, sondern führt diesen Vorgang im Kontext des aktuellen Threads aus, deshalb dauert es immer noch 10 Sekunden.

Ihr Beispiel würde:

procedure TfrmLabImageViewer.FormDestroy(Sender: TObject); 
    var BrowserKillerThread: TBackgroundBrowserKillerThread; 
begin 
    BrowserKillerThread := TBackgroundBrowserKillerThread.Create(true); 
    BrowserKillerThread.FreeOnTerminate := true; 
    BrowserKillerThread.Start; 
end; 

procedure TBackgroundBrowserKillerThread.Execute(); 
begin 
    TWinControl(WebBrowser).Parent := nil; // I don't think you need to nil the parent here, but probably does no harm 
    FreeAndNil(WebBrowser); 
end; 

nun das Einfädeln ist richtig, aber die Logik ist immer noch falsch.Wie Sie bereits bemerkt haben, dürfen Sie VCL-Objekte von anderen Threads als dem Hauptthread nicht modifizieren, dies schließt Free ein. Sie müssten dies mit Synchronize tun, die den Zweck des Threading besiegen würde.

Ich denke auch, dass die Idee, den Browser in einem Thread zu töten, weil es sonst zu lange dauert, nur die Symptome bekämpft, anstatt die Ursache. Ich denke, es wäre am besten herauszufinden, warum das überhaupt so lange dauert, und das zu beheben, anstatt zu versuchen, das Problem auf diese Weise zu umgehen. Aber das ist außerhalb des Bereichs dieser Frage, wenn Sie Probleme damit haben, sollten Sie eine separate Frage für dieses spezielle Problem stellen.

+0

Toller und hilfreicher Kommentar. Vielen Dank! – kdtop

+0

Ich habe ein bisschen damit gespielt. Ich benutze Delphi 10. Es sieht aus wie BrowserKillthread.Start sollte stattdessen BrowserKillthread.Resume sein. Es gibt keine .Starte in meiner Version von Delphi. Nochmals vielen Dank ... – kdtop

+0

Ich entschied mich, dem Rat von DNR zu folgen, vor allem, weil MartynA sagte, dass er keine Verzögerung mit dem Schließen hatte, und ging nach der zugrunde liegenden Ursache. Ich öffnete den Acrobat-Reader über das Windows-Startmenü und ging in die Bearbeitungseinstellungen. Ich habe jede Einstellung, die ich finden konnte, ausgeschaltet, die auf das Sprechen mit Sicherheitsservern usw. Bezug nahm, und eine Sicherheitsausnahme für den Ordner gemacht, von dem ich meine .pdfs geladen hatte (nur lokale Dateien anzeigen) ... Und tatsächlich, die 10 Sekunden Verzögerung ist weg. Ich schreibe das hier für den Fall, dass jemand anderes die gleichen Probleme hat. Nochmals vielen Dank für die Hilfe. – kdtop