【问题标题】:delphi thread waitfor infinite (never terminate)delphi线程等待无限(永不终止)
【发布时间】:2017-12-13 14:19:21
【问题描述】:

我正在等待线程完成,但是没有成功,它卡在了WaitFor()方法中;并且不回来,无限期地站在那里。

procedure TForm1.btnStopClick(Sender: TObject);

谁能帮帮我?

我正在使用 Delphi Berlin 10.1 Update 2,在 Windows 10 64 位版本 1709 16299.64 上运行

按照代码:

unit untPrincipal;

interface

uses
   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
   Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

const
   WM_TEST_SERVICE = WM_APP + 1;

type
   TForm1 = class(TForm)
      btnStart: TButton;
      mmoOutput: TMemo;
      btnStop: TButton;
      procedure btnStartClick(Sender: TObject);
      procedure btnStopClick(Sender: TObject);
   private
      { Private declarations }
      threadService: TThread;
      procedure OnThreadTerminate(Sender: TObject);
      procedure WMTestService(var msg: TMessage); message WM_TEST_SERVICE;
   public
      { Public declarations }
   end;

   IThreadInfo = interface
      ['{B179712B-8B14-4D54-86DA-AB22227DBCAA}']
      function IsRunning: Boolean;
   end;

   IService = interface
      ['{30934A11-1FB9-46CB-8403-F66317B50199}']
      procedure ServiceCreate();
      procedure ServiceStart(const info: IThreadInfo);
   end;

   TMyService = class(TInterfacedObject, IService)
   private
      handle: THandle;
   public
      constructor Create(const handle: THandle);
      procedure ServiceCreate;
      procedure ServiceStart(const info: IThreadInfo);
   end;

   TThreadService = class(TThread)
   private
      service: IService;
   protected
      procedure Execute; override;
   public
      constructor Create(const service: IService);
   end;

   TThreadInfo = class(TInterfacedObject, IThreadInfo)
   private
      thread: TThread;
   public
      constructor Create(const thread: TThread);
      function IsRunning: Boolean;
   end;

   TThreadPost = class(TThread)
   private
      handle: THandle;
      info: IThreadInfo;
   protected
      procedure Execute; override;
   public
      constructor Create(const handle: THandle; const info: IThreadInfo);
   end;

var
   Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.btnStartClick(Sender: TObject);
var
   service: IService;
begin
   service := TMyService.Create(Self.handle);
   threadService := TThreadService.Create(service);
   threadService.OnTerminate := OnThreadTerminate;
   threadService.Start;
end;

procedure TForm1.btnStopClick(Sender: TObject);
begin
   if Assigned(threadService) then
   begin
      try
         threadService.Terminate;
         threadService.WaitFor;
      finally
         if Assigned(threadService) then
            FreeAndNil(threadService);
      end;
   end;
end;

procedure TForm1.OnThreadTerminate(Sender: TObject);
begin
   mmoOutput.Lines.Add(DateTimeToStr(Now()) + ' - procedure TForm1.OnThreadTerminate(Sender: TObject);');
end;

procedure TForm1.WMTestService(var msg: TMessage);
begin
   mmoOutput.Lines.Add(DateTimeToStr(Now()) + ' - Service');
end;

{ TMyService }

constructor TMyService.Create(const handle: THandle);
begin
   inherited Create();
   Self.handle := handle;
end;

procedure TMyService.ServiceCreate;
begin
   PostMessage(handle, WM_TEST_SERVICE, 0, 0);
end;

procedure TMyService.ServiceStart(const info: IThreadInfo);
var
   thread: TThreadPost;
begin
   while info.IsRunning do
   begin
      thread := TThreadPost.Create(handle, info);
      try
         thread.Start;
         thread.WaitFor;
         ShowMessage('Never Execute');
      finally
         thread.Free;
      end;
   end;
end;

{ TThreadService }

constructor TThreadService.Create(const service: IService);
begin
   inherited Create(True);
   Self.service := service;
end;

procedure TThreadService.Execute;
begin
   service.ServiceCreate;
   service.ServiceStart(TThreadInfo.Create(Self) as IThreadInfo);
end;

{ TThreadInfo }

constructor TThreadInfo.Create(const thread: TThread);
begin
   inherited Create();
   Self.thread := thread;
end;

function TThreadInfo.IsRunning: Boolean;
begin
   Result := not thread.CheckTerminated;
end;

{ TThreadPost }

constructor TThreadPost.Create(const handle: THandle; const info: IThreadInfo);
begin
   inherited Create(True);
   Self.handle := handle;
   Self.info := info;
end;

procedure TThreadPost.Execute;
begin
   while info.IsRunning do
   begin
      PostMessage(handle, WM_TEST_SERVICE, 0, 0);
      Sleep(1000);
   end;
end;

end.

【问题讨论】:

  • 你为什么要在启动线程后调用它?为什么在那种情况下有一个线程?为什么线程从不检查Terminated?
  • 这段代码只是为了显示我在一个更大的系统中遇到的一个问题,当我完成系统时,我在运行多个 IService 并且每个都有自己的线程(Windows 服务以防万一)我必须结束所有线程,等待相同的完成并执行其他操作,问题是我何时必须完成。每个 IService 的实现方式不同,在某些情况下,它内部运行的线程更多,这就是我发现问题的地方
  • @JerryDodge,你说WaitFor()方法是不必要的,但是此时我如何等待Thread结束呢?谢谢
  • 主表单句柄可以随时重新创建。使用您自己的句柄来发布消息。见Is the TForm.Handle thread safe?。从线程调用ShowMessage 是错误的,因为vcl 不是线程安全的。
  • @LURD 感谢您的评论,但 ShowMessage 从未运行,因为代码在 WaitFor () 方法中停止。在 Handle 上,这只是 Threads 和 PostMessage 的问题,以至于当代码在 WaitFor() 方法中停止时,PostMessage 继续工作

标签: multithreading delphi delphi-10.1-berlin


【解决方案1】:

你在打电话:

function TThreadInfo.IsRunning: Boolean;
begin
   Result := not thread.CheckTerminated;
end;

来自TThreadPost.Execute,它尝试检查thread 实例是否已终止。

问题在于对CheckTerminated 的调用使用当前线程终止状态,而不是thread 实例。 (想想如果thread 实例在thread.CheckTerminated 被调用时被终止并释放会发生什么,如果可能的话)。

结果是IsRunning 永远不会是假的,你将有一个无限循环。 您将不得不重新设计如何以安全的方式停止线程。

【讨论】:

  • 当我看到你的答案时,我正要发布我的答案。我花了 10 分钟来决定是否应该发帖。
  • @NasreddineAbdelillahGalfout,没关系。目前我不能推荐一个好的解决方案,除了重新设计。像这样将线程和复杂的逻辑嵌套在一起并不是一个好的解决方案。我总是尝试在生产者/消费者方案中分离职责,并且逻辑/数据应该只传递,而不是共享。
  • 是的,重新设计将是去这里的方式。我想我的回答涵盖了为什么会发生僵局。我会把解决方案留给你。 :)
  • @LURD 谢谢,简单的改个控制变量检查,问题就解决了
【解决方案2】:

在我们在这里开始之前,请下次在您的班级中以“F”开头的字段名称。

让我们从第一个用户操作开始逐步检查您的代码

procedure TForm1.btnStartClick(Sender: TObject);
  1. service := TMyService.Create(Self.handle);

您创建TMyService 的实例并将TForm.Handle 分配给字段handle(您应该将其命名为FHandle)。

  1. threadService := TThreadService.Create(service);

您创建一个暂停的TThread 实例并将service 分配给它的私有字段service(同样您应该将其命名为FService,并且您不需要使用self)

有一件事是,这次的引用与第一行不同,第一行引用在范围结束时死亡/丢失。

  1. threadService.OnTerminate := OnThreadTerminate;

分配 Onterminate 事件处理程序。

Onterminate 在内部使用 synchronize(),因此这可能是导致死锁的原因。 (

  1. threadService.Start;

你启动了暂停的threadService。

此时您有两个线程运行 MainThread 和 threadService(MyService 在 MainThread 的上下文中运行)。

MainThread 处于空闲状态,等待更多用户操作或处理其他消息 (即:重绘、调整大小、移动表格……等)。

threadService 正在运行它的 execute 方法。

现在让我们关注TThreadService.Execute; 在做什么

  1. service.ServiceCreate;

在这里发消息给handle == TForm.Handle(

  1. service.ServiceStart(TThreadInfo.Create(Self) as IThreadInfo);

    2.1while info.IsRunning do

您的问题出现在这里,因为info.IsRunning 检查当前线程中的终止标志(否则会在内部引发异常),即threadService(

 2.2 **the catastrophe code**

    begin
      thread := TThreadPost.Create(handle, info);
      try
         thread.Start;
         thread.WaitFor;
         ShowMessage('Never Execute');
      finally
         thread.Free;
      end;
   end;

在这里你创建TThreadPost 这是另一个线程并启动它。然后你调用waitfor 锁定TThreadService。 所以现在你有三个线程在运行:MainThread(空闲)、threadService(死锁)和TThreadPost(松散)。

在TThreadPost执行方法中还有另一个while info.IsRunning do

检查终止标志,但它是TThreadPost 的标志,而不是threadService 的标志。

所以当用户点击停止按钮时,MainThread 中的waitfor 调用正在等待死锁线程。

作为 LU RD 所说的解决方案(当他发布他的答案时,我正在写我的答案)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 2011-10-23
    • 1970-01-01
    相关资源
    最近更新 更多