Windows 有没有办法在没有收到信息的情况下睡觉?
我在一个主循环如下所示的服务中工作:Windows 有没有办法在没有收到信息的情况下睡觉?,windows,delphi,winapi,service,Windows,Delphi,Winapi,Service,我在一个主循环如下所示的服务中工作: while (fServer.ServerState = ssStarted) and (Self.Terminated = false) do begin Self.ServiceThread.ProcessRequests(false); ProcessFiles; Sleep(3000); end; ProcessRequests非常类似于应用程序.ProcessMessages。我无法将true传递给它,因为如果我这样做,它
while (fServer.ServerState = ssStarted) and (Self.Terminated = false) do
begin
Self.ServiceThread.ProcessRequests(false);
ProcessFiles;
Sleep(3000);
end;
ProcessRequests非常类似于应用程序.ProcessMessages
。我无法将true
传递给它,因为如果我这样做,它将阻塞,直到收到来自Windows的消息,ProcessFiles将不会运行,并且它必须持续运行。睡眠是用来降低CPU使用率的
在我尝试从Windows的服务管理列表中关闭服务之前,这一切都很正常。当我点击停止时,它会发送一条消息,并希望立即得到响应,如果它处于睡眠命令的中间,Windows将给我一个错误,即服务没有响应停止命令。
所以我需要说的是“睡眠3000或直到你收到一条消息,以先到者为准。”我肯定有一个API用于此,但我不确定它是什么。有人知道吗?使用计时器运行ProcessFiles,而不是将其侵入主应用程序循环。然后ProcessFiles将在您想要的时间间隔内运行,消息将被正确处理,而不占用100%的CPU。您不需要睡眠整整3秒钟来保持低CPU使用率。即使是像Sleep(500)这样的东西也应该让你的使用率保持在相当低的水平(如果没有消息等待处理,那么它应该很快通过循环并再次进入睡眠状态。如果你的循环需要几毫秒的时间来运行,这仍然意味着你的线程大部分时间都在睡眠状态
话虽如此,您的代码可能会从一些重构中受益。您说您不希望ProcessRequests阻止等待消息?该循环中唯一的另一件事是ProcessFiles。如果它依赖于正在处理的消息,那么为什么它不能阻止?如果它不依赖于正在处理的消息,那么它可以被拆分吗o另一个线程?(前面关于如何通过计时器触发ProcessFile的建议是一个很好的建议)。这种东西很难正确理解,所以我通常从MSDN的API文档开始 该文件特别针对此类情况: 调用等待时请小心 直接或间接 间接创建窗口。如果 线程创建任何窗口,它必须 处理消息。消息广播 发送到系统中的所有窗口。 使用等待函数的线程 没有超时间隔可能会导致 系统将陷入僵局 间接 创建的窗口是DDE和 函数。因此,如果 你有一个线程可以创建 窗口,使用 或者更确切地说 而不是WaitForSingleObject 在中,有一个
dwWakeMask
参数指定要返回的队列消息,还有一个表描述可以使用的掩码
编辑,原因是:
如果您的主循环由于、或而可以继续,那么您可以使用
--jeroen我在多线程应用程序中使用了TTimer,结果很奇怪,所以现在我使用事件
while (fServer.ServerState = ssStarted) and (Self.Terminated = false) do
begin
Self.ServiceThread.ProcessRequests(false);
ProcessFiles;
if ExitEvent.WaitFor(3000) <> wrTimeout then
Exit;
end;
现在最后一件事是在服务停止时触发事件。我认为服务的停止事件是放置此事件的正确位置
ExitEvent.SetEvent;
在我的DB连接池系统中,我将此代码用于清理线程,但在您的情况下,它也应该工作得很好。使用一个TEvent,当线程应该唤醒时发出信号。然后阻塞TEvent(如果您有多个事件要等待,请使用Jeroen所说的waitformultiple)
while (fServer.ServerState = ssStarted) and (not Self.Terminated) do
begin
ProcessFiles;
if MsgWaitForMultipleObjects(0, nil, FALSE, 3000, QS_ALLINPUT) = WAIT_OBJECT_0 then
Self.ServiceThread.ProcessRequests(false);
end;
如果您希望以3秒的间隔调用ProcessFiles(),而不考虑任何消息的到达,则可以使用可等待计时器,即:
var
iDue: TLargeInteger;
hTimer: array[0..0] of THandle;
begin
iDue := -30000000; // 3 second relative interval, specified in nanoseconds
hTimer[0] := CreateWaitableTimer(nil, False, nil);
SetWaitableTimer(hTimer[0], iDue, 0, nil, nil, False);
while (fServer.ServerState = ssStarted) and (not Self.Terminated) do
begin
// using a timeout interval so the loop conditions can still be checked periodically
case MsgWaitForMultipleObjects(1, hTimer, False, 1000, QS_ALLINPUT) of
WAIT_OBJECT_0:
begin
ProcessFiles;
SetWaitableTimer(hTimer[0], iDue, 0, nil, nil, False);
end;
WAIT_OBJECT_0+1: Self.ServiceThread.ProcessRequests(false);
end;
end;
CancelWaitableTimer(hTimer[0]);
CloseHandle(hTimer[0]);
end;
无法将ProcessFiles移动到单独的线程吗?在主线程中,您只需等待消息,当服务被终止时,您就可以终止ProcessFiles线程。这是我尝试的第一件事,但它不起作用。我将其取出并替换为睡眠循环,我同意这是一个丑陋的攻击。但我只是尝试了用定时器再次启动,(自从我这么做以来,很多东西都改变了),现在它可以工作了。想想看,这不适合一个service@mj2008:最初,这根本不应该是一项服务。但你知道范围爬行是怎样的…:(可能在这种情况下,但一旦你开始使用线程,TTimer就会给你带来一大堆惊喜。VCL应用程序中周期性触发TTimer是真正有趣的东西的潜在来源。特别是当你认为应该在应用程序代码中的某个地方添加对Application.ProcessMessages的调用时。记住这一天下一个奇怪的错误出现了。我在我的服务中完全是这样做的,但是保持睡眠时间短。CPU仍然被调度程序关闭。尝试睡眠(500)你想在后台线程中一直睡到收到消息,还是想冻结前台线程?请在这里告诉我。你可以试试SleepEx,或者如Jeroen所建议的WaitForMultipleObjects。SleepEx是为可中断睡眠而设计的。这是一种明智的方式。其他方式并不是真正的“等待”在后台线程中,他们周期性地在前台线程中做一些事情。这两件事不是做同一件事的替代品,而是完全不同的事情。使用线程或不使用线程,但如果你正在使用线程并等待消息,你确实需要它是可中断的。SleepEx也是可中断的t遇到了与原始版本相同的问题-它正在等待整整3秒钟,因为在ProcessRequests()之前无法发出ExiteEvent信号调用来处理SCM的停止请求。已经涵盖了这一点。ProcessFiles基本上只是查看是否有任何文件要处理,然后将它们交给另一个线程。@Mason Wheeler:这不会阻止您将ProcessFiles移动到另一个线程,或者在文件处理线程被处理后让它自己调用ProcessFiles一个。
var
iDue: TLargeInteger;
hTimer: array[0..0] of THandle;
begin
iDue := -30000000; // 3 second relative interval, specified in nanoseconds
hTimer[0] := CreateWaitableTimer(nil, False, nil);
SetWaitableTimer(hTimer[0], iDue, 0, nil, nil, False);
while (fServer.ServerState = ssStarted) and (not Self.Terminated) do
begin
// using a timeout interval so the loop conditions can still be checked periodically
case MsgWaitForMultipleObjects(1, hTimer, False, 1000, QS_ALLINPUT) of
WAIT_OBJECT_0:
begin
ProcessFiles;
SetWaitableTimer(hTimer[0], iDue, 0, nil, nil, False);
end;
WAIT_OBJECT_0+1: Self.ServiceThread.ProcessRequests(false);
end;
end;
CancelWaitableTimer(hTimer[0]);
CloseHandle(hTimer[0]);
end;