【问题标题】:Is this Delphi Thread code correct?这个德尔福线程代码正确吗?
【发布时间】:2017-06-26 20:41:41
【问题描述】:

我知道 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;

问题:

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

提前非常感谢。

KT

【问题讨论】:

  • 这永远行不通。除了主线程之外,您无法从任何地方访问任何类型的 VCL 控件,并且您无法在一个线程中创建它并在另一个线程中销毁它。你写的代码并没有改变任何东西——浏览器仍然被拥有它的主线程破坏;您只是从您的线程中调用该代码(这是错误的,除非您通过同步来执行此操作)。
  • 另外...您可能想再次查看所有这些主题。我敢打赌,您还没有看到一个从外部调用执行方法的示例。为什么?因为这样execute方法中的代码将在调用它的线程的上下文中运行。
  • 使用 TWebBrower + v.15.023 of Acro Reader DC 关闭您描述的应用程序时,我没有收到延迟。您能否举一个导致您延迟的示例 URL?
  • 您是否尝试在关闭表单之前导航到 about:blank?
  • 感谢大家的帮助。 @MartnyA:我正在打开一个存储在本地文件系统中的 .pdf 文件。

标签: multithreading delphi acrobat twebbrowser


【解决方案1】:

我对 .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 执行此操作,这会破坏线程的目的。

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

【讨论】:

  • 伟大而有用的评论。非常感谢!
  • 我一直在玩这个。我正在使用 Delphi 10。看起来 BrowserKillthread.Start 应该是 BrowserKillthread.Resume。我的 Delphi 版本中没有 .Start。再次感谢...
  • 我决定听从 DNR​​ 的建议,尤其是 MartynA 说他没有遇到关闭延迟,并追查了根本原因。我从 Windows 开始菜单打开 Acrobat 阅读器,然后进入编辑首选项。我关闭了所有我能找到的涉及与安全服务器等通信的设置,并为我从中加载我的 .pdf 的文件夹设置了一个安全例外(仅查看本地文件)......果然, 10 秒的延迟消失了。我在这里写这个,以防其他人有同样的问题。再次感谢大家的帮助。
  • Resume 在 XE 中已被弃用,文档声明您应该使用 Start 代替。如果 Delphi 10 是指最近的版本之一(西雅图或柏林),则应该有 start 方法。
  • 我在 Help->about 中查看并看到了这个版本:版本 10.0.2166.28377 但我在发布后意识到这实际上是 Delphi 2006。抱歉造成混淆。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-08
  • 1970-01-01
  • 2015-02-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多