这个 Delphi 线程代码是否正确?

Is this Delphi Thread code correct?

我知道 Delphi 个话题已经在很多话题上讨论过了。我尝试查看它们,但没有找到问题的答案。

背景: 我发现在浏览器加载 Adob​​e Acrobat Reader DC 后释放 TWebBrowser 可能需要 10 秒以上。我认为它正在以某种方式检查更新或其他内容。试图关闭带有浏览器的表单时很烦人。

我想也许我可以让后台线程释放浏览器。所以我把浏览器变量移到了一个全局变量中(私密地存放在单元的实现部分)。一次只能使用其中一种形式。然后我试图让一个线程在后台释放它。它没有像我预期的那样工作。

示例代码

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;

问题:

非常感谢。

KT

Is my understanding wrong of what .execute does??

是的。这不是你做线程的方式。通常,您首先必须决定在最初启动线程后是否需要对该线程执行任何操作。如果是这样,请保留引用,不要使用 FreeOnTerminate,并在线程终止后自行处理线程的销毁。没有的话就别留reference了,设置FreeOnTerminate,发过去

您既不会在线程的执行中设置 FreeOnTerminate,也不会保留对具有此设置的线程的全局引用。您也不会通过调用线程的 Execute 方法来启动线程,而是通过创建线程 non-suspended 或调用 Start 来启动线程。只是调用 Execute 并没有真正启动线程,而是在当前线程的上下文中执行这个过程,这就是为什么它仍然需要 10 秒。

您的示例将变为:

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;

现在线程是对的,但是逻辑还是不对。正如您已经注意到的,您不能从主线程以外的线程修改 VCL 对象,这包括 Free。您必须使用 Synchronize 执行此操作,这会破坏线程的目的。

我还认为在一个线程中终止浏览器的想法,否则它会花费太长时间,这只是在与症状作斗争,而不是与原因作斗争。我认为最好首先找出为什么需要这么长时间,然后解决这个问题,而不是试图以这种方式规避问题。但这超出了这个问题的范围,如果你有问题,你应该针对这个特定问题提出一个单独的问题。