我们有一个非常成熟的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应用程序运行,那很好!
据我所知,OnTerminate事件没有被调用.
您需要提供更多数据.
请注意,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.
正如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);
等待永远.
因此,虽然上面的修复有效,但还有更深层的内容!