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

时间:2017-02-09 01:09:56

标签: multithreading delphi acrobat twebbrowser

我知道很多线程都讨论过Delphi线程。我试过检查它们,但没有找到我的问题的答案。

背景 我发现在浏览器加载Adobe 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;

问题:

  • 当我在调试模式中单步执行FormDestroy代码时,包含BrowserKillerThread.Execute()的行;执行还需要10秒钟。我原以为这会启动另一个线程并立即返回。但事实并非如此。我的理解是错误的.execute是什么?或者是有趣的事情?
  • 我做的是坏事吗?我已经读过VCL不是线程安全的,并且不能/不应该从另一个线程访问VCL对象。我希望这不适用于这种情况,因为我只是在没有计划进一步交互的情况下释放对象。
  • 如果一个线程在终止时自由,我认为这会让我的BrowserKillerThread指针悬空。所以可以像我一样分配nil吗?
  • 关于如何做得更好的任何建议?

提前非常感谢。

KT

1 个答案:

答案 0 :(得分:8)

  

我的理解是错误的.execute是什么?

是。这不是你如何做线程。通常,您首先必须决定在最初启动它之后是否需要对该线程执行任何操作。如果是这样,请保留引用,不要使用FreeOnTerminate,并在终止后自行处理线程的破坏。如果不这样做,请不要保留参考,设置FreeOnTerminate,然后将其发送。

您既不会在线程的执行中设置FreeOnTerminate,也不会对具有此设置的线程保持全局引用。您也不是通过调用它的Execute方法来启动线程,而是通过创建非挂起或调用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来做这件事,这会破坏线程化的目的。

我也认为在线程中杀死浏览器的想法,因为否则花费太长时间只是在与症状作斗争,而不是与原因作斗争。我认为最好先找出为什么这么长时间,并解决这个问题,而不是试图以这种方式绕过这个问题。但这超出了本问题的范围,如果您遇到问题,则应针对此特定问题提出单独的问题。