除非收到消息,否则有办法入睡吗?

时间:2010-07-28 17:38:39

标签: windows delphi winapi service

我正在使用一个主循环如下的服务:

while (fServer.ServerState = ssStarted) and (Self.Terminated = false) do
begin
  Self.ServiceThread.ProcessRequests(false);
  ProcessFiles;
  Sleep(3000);      
end;

ProcessRequests很像Application.ProcessMessages。我无法将true传递给它,因为如果我这样做,它会阻塞,直到从Windows收到消息,并且ProcessFiles将无法运行,并且必须不断运行。 Sleep可以降低CPU使用率。

这很好用,直到我尝试从Windows的服务管理列表关闭服务。当我点击Stop时,它会发送一条消息,并希望几乎立即得到一个响应,如果它在Sleep命令的中间,Windows会给我一个错误,该服务没有响应Stop命令。

所以我需要说的是“睡觉3000或者直到收到消息,以先到者为准”。我确定有一个API,但我不确定它是什么。有谁知道吗?

7 个答案:

答案 0 :(得分:11)

这种东西很难做到,所以我通常从MSDN的API文档开始。

WaitForSingleObject文档专门针对这些情况指示MsgWaitForMultipleObjects

  

呼叫等待时要小心   函数和代码直接或   间接创建窗口。如果一个   线程创建任何窗口,它必须   处理消息。消息广播   被发送到系统中的所有窗口。   使用等待函数的线程   没有超时间隔可能会导致   系统陷入僵局。二   间接代码的例子   创建窗口是DDE和   CoInitialize功能。因此,如果   你有一个创建的线程   窗口,使用MsgWaitForMultipleObjects   或者MsgWaitForMultipleObjectsEx   比WaitForSingleObject

MsgWaitForMultipleObjects中,您有一个dwWakeMask参数指定要返回的排队邮件,以及一个描述您可以使用的掩码的表。

由于Warren P的评论

修改

如果您的主循环因ReadFileExWriteFileExQueueUserAPC而继续,那么您可以使用SleepEx

- 的Jeroen

答案 1 :(得分:8)

MsgWaitForMultipleObjects()是要走的路,即:

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;

答案 2 :(得分:2)

使用计时器运行ProcessFiles,而不是将其入侵主应用程序循环。然后ProcessFiles将以您想要的间隔运行,并且消息将被正确处理,而不是占用100%的CPU。

答案 3 :(得分:1)

我在多线程应用程序中使用了TTimer,结果很奇怪,所以现在使用Events。

while (fServer.ServerState = ssStarted) and (Self.Terminated = false) do
begin
  Self.ServiceThread.ProcessRequests(false);
  ProcessFiles;

  if ExitEvent.WaitFor(3000) <> wrTimeout then
    Exit;   
 end;

使用

创建活动
ExitEvent := TEvent.Create(nil, False, False, '');

现在最后一件事是在服务停止时触发事件。我认为服务的Stop事件是放置它的正确位置。

ExitEvent.SetEvent;

我在我的数据库连接池系统中使用此代码作为清理线程,但它也适用于您的情况。

答案 4 :(得分:0)

您无需睡眠3秒钟以保持较低的CPU使用率。即使像Sleep(500)这样的东西也应该保持你的使用率非常低(如果没有消息等待处理它应该很快通过循环再次进入睡眠状态。如果你的循环需要几毫秒来运行它仍然意味着你的线程大部分时间都在睡觉。

话虽如此,您的代码可能会受益于一些重构。您说您不希望ProcessRequests阻止等待消息?该循环中唯一的另一件事是ProcessFiles。如果这取决于正在处理的消息那么为什么它不能阻止?如果它不依赖于正在处理的消息,那么它可以拆分到另一个线程吗? (之前通过计时器触发ProcessFiles的建议是关于如何执行此操作的极好建议。)

答案 5 :(得分:0)

使用在线程唤醒时发出信号的TEvent。然后阻止tevent(使用waitformultiple作为Jeroen说如果有多个事件要等待)

答案 6 :(得分:0)

是否无法将ProcessFiles移动到单独的线程?在MainThread中,您只需等待消息,当服务终止时,您将终止ProcessFiles线程。