我们有一个相当成熟的COM dll,我们使用DUnit进行测试。我们最近的一个测试创建了一些线程,并从这些线程测试对象。使用gui前端运行测试时此测试工作正常,但在作为控制台应用程序运行时挂起。这是我们在测试中所拥有的快速伪视图
SetupTest;
fThreadRefCount := 0; //number of active threads
Thread1 := TMyThread.Create(True);
Inc(fThreadRefCount);
Thread1.OnTerminate := HandleTerminate; //HandleOnTerminate decrements fThreadRefCount
Thread3 := TMyThread.Create(True);
Inc(fThreadRefCount);
Thread2.OnTerminate := HandleTerminate; //HandleOnTerminate decrements fThreadRefCount
Thread3 := TMyThread.Create(True);
Inc(fThreadRefCount);
Thread3.OnTerminate := HandleTerminate; //HandleOnTerminate decrements fThreadRefCount
Thread1.Resume;
Thread2.Resume;
Thread3.Resume;
while fThreadRefCount > 0 do
Application.ProcessMessages;
我在OnExecute中没有尝试过任何操作,所以我确定这不是我正在测试的实际代码。在控制台中,fThreadRefCount永远不会减少,而如果我将它作为gui app运行,那很好!
据我所知,OnTerminate事件没有被调用。
答案 0 :(得分:8)
您需要提供更多数据。
请注意,OnTerminate是通过Synchronize调用的,它需要在某处某处调用CheckSynchronize。 Application.ProcessMessages通常会这样做,但是根据VCL的初始化方式,同步机制可能还没有在控制台应用程序中完全连接在一起。
无论如何,这个程序在我的机器上按预期工作:
uses Windows, SysUtils, Classes, Forms;
var
threadCount: Integer;
type
TMyThread = class(TThread)
public
procedure Execute; override;
class procedure Go;
class procedure HandleOnTerminate(Sender: TObject);
end;
procedure TMyThread.Execute;
begin
end;
class procedure TMyThread.Go;
function MakeThread: TThread;
begin
Result := TMyThread.Create(True);
Inc(threadCount);
Result.OnTerminate := HandleOnTerminate;
end;
var
t1, t2, t3: TThread;
begin
t1 := MakeThread;
t2 := MakeThread;
t3 := MakeThread;
t1.Resume;
t2.Resume;
t3.Resume;
while threadCount > 0 do
Application.ProcessMessages;
end;
class procedure TMyThread.HandleOnTerminate(Sender: TObject);
begin
InterlockedDecrement(threadCount);
end;
begin
try
TMyThread.Go;
except
on e: Exception do
Writeln(e.Message);
end;
end.
答案 1 :(得分:5)
正如Barry正确指出的那样,除非调用CheckSyncronize,否则不会调用Synchronize,如果未调用synchronize,则不会触发OnTerminate事件。似乎正在发生的事情是,当我将单元测试作为控制台应用程序运行时,消息队列上没有消息,因此从Processmessages调用的Application.ProcessMessage永远不会调用checkSynchronize。我现在通过将循环更改为
来解决问题While fThreadRefCount > 0 do
begin
Application.ProcessMessages;
CheckSynchronize;
end;
它现在可以在控制台和gui模式下工作。
整个wakeupmainthread挂钩似乎设置正确。这个钩子发布了触发检查同步的WM_NULL消息。它只是在控制台应用程序中没有那么远。
更多调查
因此,同步会被调用。 DoTerminate调用Synchronize(CallOnTerminate),但那里有一行:
WaitForSingleObject(SyncProcPtr.Signal, Infinite);
等待永远。
因此,虽然上面的修复有效,但还有更深层次的内容!