为什么在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()
在工作线程的上下文中运行。
我有一个问题,我的 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()
在工作线程的上下文中运行。