TThread 在 Delphi 2006 控制台应用程序中的工作方式是否不同?

发布于 2024-07-08 17:40:01 字数 914 浏览 6 评论 0原文

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

We have a pretty mature COM dll, which we test using DUnit. One of our recent tests creates a few threads, and tests the object from those threads. This test works fine when running the test using the gui front-end, but hangs when running as a console application. Here's a quick pseudo view of what we have in the test

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;

I have tried doing nothing in the OnExecute, so I'm sure it's not the actual code I'm testing. In the console, fThreadRefCount never decrements, while if I run it as a gui app, it's fine!

As far as I can see, the OnTerminate event is just not called.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦太阳 2024-07-15 17:40:01

您需要提供更多数据。

请注意,OnTerminate 是通过 Synchronize() 调用的,这需要在某个时刻调用 CheckSynchronize()Application.ProcessMessages() 通常会执行此操作,但根据 VCL 的初始化方式,Synchronize() 机制可能尚未完全连接在一起控制台应用程序。

无论如何,这个程序在我的机器上按预期工作:

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.

You need to provide more data.

Note that OnTerminate is called via Synchronize(), which requires a call to CheckSynchronize() at some point somewhere. Application.ProcessMessages() normally does this, but depending on how the VCL has been initialized, it's possible that the Synchronize() mechanism hasn't been fully hooked together in a Console application.

In any case, this program works as expected on my machine:

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.
停滞 2024-07-15 17:40:01

正如 Barry 正确指出的那样,除非调用 CheckSyncronize(),否则不会处理 Synchronize(),并且如果不处理 Synchronize(),那么 OnTerminate 事件不会被触发。

似乎发生的情况是,当我作为控制台应用程序运行单元测试时,消息队列上没有消息,因此从 Application 调用的 Application.ProcessMessage() .ProcessMessages(),永远无法调用CheckSynchronize()

我现在通过将循环更改为以下方式解决了问题:

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

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

整个 WakeupMainThread 钩子似乎设置正确。 正是这个钩子发布了触发 CheckSynchronize()WM_NULL 消息。 它只是在控制台应用程序中没有达到那么远。

更多调查

因此,Synchronize() 确实被调用。 DoTerminate() 调用 Synchronize(CallOnTerminate) 但其中有一行:

WaitForSingleObject(SyncProcPtr.Signal, Infinite); 

它只是永远等待。

因此,虽然我的上述修复有效,但还有更深层次的内容!

As Barry rightly pointed out, unless CheckSyncronize() is called, Synchronize() is not processed, and if Synchronize() is not processed, then the OnTerminate event is not fired.

What seems to be happening is that when I run my unit tests as a Console application, there are no messages on the message queue, and thus Application.ProcessMessage(), which is called from Application.ProcessMessages(), never gets to call CheckSynchronize().

I've now solved the problem by changing the loop to this:

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

It now works in both Console and GUI modes.

The whole WakeupMainThread hook seems to be setup properly. It's this hook which posts the WM_NULL message that triggers the CheckSynchronize(). It just doesn't get that far in the Console app.

More Investigation

So, Synchronize() does get called. DoTerminate() calls Synchronize(CallOnTerminate) but there's a line in there:

WaitForSingleObject(SyncProcPtr.Signal, Infinite); 

which just waits forever.

So, while my fix above works, there's something deeper to this!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文