6

我们有一个相当成熟的 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 事件只是没有被调用。

4

2 回答 2

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.
于 2008-11-07T19:07:41.607 回答
5

正如 Barry 正确指出的那样,除非调用 CheckSynchronize,否则不会调用 Synchronize,如果不调用 synchronize,则不会触发 OnTerminate 事件。似乎正在发生的事情是,当我将单元测试作为控制台应用程序运行时,消息队列中没有消息,因此从 Processmessages 调用的 Application.ProcessMessage 永远不会调用 checkSynchronize。我现在通过将循环更改为

While fThreadRefCount > 0 do
begin
   Application.ProcessMessages;
   CheckSynchronize;
end;

它现在可以在控制台和 gui 模式下工作。

整个wakeupmainthread 钩子似乎设置正确。正是这个钩子发布了触发检查同步的 WM_NULL 消息。它在控制台应用程序中并没有那么远。

更多调查

所以 Synchronize确实被调用了。DoTerminate 调用 Synchronize(CallOnTerminate) 但那里有一行:

WaitForSingleObject(SyncProcPtr.Signal, Infinite); 

永远等待。

因此,虽然我的上述修复工作有效,但还有更深层次的东西!

于 2008-11-09T15:07:05.273 回答