Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Windows 有没有办法在没有收到信息的情况下睡觉?_Windows_Delphi_Winapi_Service - Fatal编程技术网

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;