如何使用最少的资源实现定期检查某些内容的线程?

13

我希望有一个后台线程,每隔一定时间(例如5秒)检查与某个服务器的连接状态。

我不知道是否有一个良好的“设计模式”来实现此功能?如果我没记错的话,我曾经在某个地方读到过在线程执行方法中休眠线程不是很好的做法。但是我可能错了。

此外,我可以使用普通的TThread类或OTL线程库。

有什么好的想法吗?

谢谢。

5个回答

16

OmniThreadLibrary中,您需要这样做:

uses
  OtlTask,
  OtlTaskControl;

type
  TTimedTask = class(TOmniWorker)
  public
    procedure Timer1;
  end;

var
  FTask: IOmniTaskControl;

procedure StartTaskClick;
begin
  FTask := CreateTask(TTimedTask.Create())
    .SetTimer(1, 5*1000, @TTimedTask.Timer1)
    .Run;
end;

procedure StopTaskClick;
begin
  FTask.Terminate;
  FTask := nil;
end;

procedure TTimedTask.Timer1;
begin
  // this is triggered every 5 seconds
end;

关于在Execute方法中休眠的问题,这取决于你的做法。如果你使用Sleep函数,那么这可能不是很明智的选择(例如因为它会阻止线程在休眠期间停止)。通过WaitForSingleObject函数进行休眠是可以的。

使用TThread和WaitForSingleObject函数的示例:

type
  TTimedThread = class(TThread)
  public
    procedure Execute; override;
  end;

var
  FStopThread: THandle;
  FThread: TTimedThread;

procedure StartTaskClick(Sender: TObject);
begin
  FStopThread := CreateEvent(nil, false, false, nil);
  FThread := TTimedThread.Create;
end;

procedure StopTaskClick(Sender: TObject);
begin
  SetEvent(FStopThread);
  FThread.Terminate;
  FThread.Free;
  CloseHandle(FStopThread);
end;

{ TTimedThread }

procedure TTimedThread.Execute;
begin
  while WaitForSingleObject(Form71.FStopThread, 5*1000) = WAIT_TIMEOUT do begin
    // this is triggered every 5 seconds
  end;
end;

OTL计时器的实现方式类似于上面的TThread代码。OTL计时器被保存在优先级列表中(基本上是按“下一次发生”的时间对计时器进行排序),TOmniWorker中的内部MsgWaitForMultipleObjects分派程序指定了最高优先级计时器的适当超时值。


谢谢gabr,我本想在你的论坛上问这个问题,但是它无法使用。+1 - Wodzu
是的,域名服务器出了点问题;我正在处理。 - gabr
你能告诉我们更多关于你如何实现时间解决方案的吗?也许写一篇博客文章会更好哦 :) - Wodzu
谢谢。当您的论坛运行时,我会深入了解更多细节。 - Wodzu
以什么样的事件形式,David?Windows事件吗?如果您编写自己的线程循环,则可以使用任何您想要的内容,但您也可以选择退出并让OTL自己处理线程循环。 - gabr

14

您可以使用一个事件,并通过带有WaitForSingleObject等待事件的循环来实现TThread子类的Execute方法,指定超时时间。这样,当需要立即唤醒线程时,例如在终止时,就可以立即唤醒线程。


1

如果线程在应用程序的生命周期内运行,可以在应用程序关闭时被操作系统简单地终止,并且不需要精确的时间控制,那么为什么要使用比sleep(5000)更多的打字解决方案呢?


4
因为这样你就可以等待长达5秒钟的线程(以及程序)终止。 - gabr
1
我只是让线程睡眠1000毫秒,然后它会醒来,检查是否已终止,然后检查是否有工作要做。该线程在系统上几乎处于空闲状态,并且工作效率很高。当然,在关闭时会有1秒的延迟,但我首先对所有线程调用Terminate,然后进行WaitFors,因此所有线程的总延迟为1秒,而不是1秒乘以线程数。由于某些线程将工作更长时间,因此成本可以忽略不计。 - mj2008
1
@Torbins等 - 如果你不销毁线程,析构函数将不会被调用,也不会有等待! - Martin James
2
@gabr 你可以等待,当然,但是为什么要这样做呢?难道你不想让你的应用立即关闭吗? - Martin James
1
据我所知,TThread.Terminate仅设置一个标志,因此不会导致任何延迟。导致延迟的是TThread.WaitFor,无论是显式调用还是从TThread.Destroy调用。如果您不调用TThread.WaitFor,则应用程序将不会被阻塞。 - Martin James
显示剩余5条评论

1

为了添加另一种实现5秒事件的方法,可以使用多媒体定时器。它类似于TTimer,但不依赖于您的应用程序。在配置它之后(您可以设置单次或重复),它会在另一个线程中回调您。由于其本质,它非常准确(精度高达1毫秒以下)。在此处查看一些Delphi示例代码

调用定时器的代码很简单,并且支持所有Windows平台。


0

4
这个方案相比于其他在此讨论的解决方案有何优劣之处?换句话说,你应该改进你的回答。 - gabr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接