为什么在dll中不调用threadTerminate

Why is threadTerminate not called inside a dll

我有一个问题,我的 dll 中的代码与普通应用程序中的相同代码相比表现不同。经过一些调试后,我发现线程的 OnTerminate 从未在 dll 中调用。

type
  TTest = class
  private
  public
     procedure threadStart();
     procedure threadEnd(Sender: TObject);
     procedure lines(value: String);
  end;

procedure TTest.threadStart();
var aThread : TThread;
begin
 aThread :=
    TThread.CreateAnonymousThread(
      procedure
      begin
         lines('start')
      end
    );
  aThread.FreeOnTerminate := True;
  aThread.OnTerminate := self.threadEnd;
  aThread.Start;
end;

procedure TTest.threadEnd;
begin
  lines('end')
end;

procedure TTest.lines(value: String);
  var MyText: TStringlist;
begin
  MyText:= TStringlist.create;
  MyText.Add(value);
  MyText.SaveToFile('.\filename.txt');
  MyText.Free
end;

如果我 运行 来自普通 VLC Delphi 应用程序的此代码,我会在文本文件中得到 end。 如果我 运行 来自 dll 的相同代码(将其静态或动态加载到 VLC 应用程序中),我会在文本文件中得到 start

我的问题:为什么?或者更好地问,我怎样才能让我的 dll 以与我的 VLC 相同的方式运行。我使用的当前版本是 XE7.

TThread.OnTerminate 事件通过调用 TThread.Synchronize() 在主 UI 线程的上下文中触发,该事件将请求存储在主 UI 的队列中线程定期检查,在可用时执行挂起的请求。

如果在启用运行时包的情况下编译 DLL 和 EXE,它们将共享一个 RTL 副本(因此需要您将 rtl.bpl 部署到您的应用程序中)。当 EXE 检查 RTL 的 Synchronize() 队列时,它将看到来自 EXE 和 DLL 的未决请求。

但是,如果它们不共享单个 RTL,则它们将使用彼此不链接的单独 RTL 副本进行编译。默认情况下,EXE 中没有任何内容检查和处理来自 DLL 的 Synchronize() 队列的未决请求,仅来自 EXE 的 Synchronize() 队列。为了解决这个问题,您必须从调用 DLL 的 RTL 的 CheckSynchronize() 函数的 DLL 中导出一个函数,然后定期调用导出 DLL 函数的 EXE,例如在计时器中。

否则,解决此问题的另一种方法是绕过触发 OnTerminate 事件的 Synchronize() 调用,方法是覆盖线程的虚拟 DoTerminate() 方法(您不能用 TThread.CreateAnonymousThread() 做)。您可以让 DoTerminate() 直接调用 OnTerminate,或者只在 DoTerminate() 内部执行您需要的操作。但无论哪种方式,您都必须确保此代码是线程安全的,因为 DoTerminate() 在工作线程的上下文中运行。