代码之家  ›  专栏  ›  技术社区  ›  Mason Wheeler

如果没有收到消息,有没有办法睡觉?

  •  3
  • Mason Wheeler  · 技术社区  · 14 年前

    我在一个服务中工作,其主循环如下:

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

    处理请求很像 Application.ProcessMessages . 我不能通过 true 因为如果我这样做,它会一直阻塞,直到从Windows收到一条消息,并且processfiles不会运行,它必须连续运行。睡眠是为了降低CPU使用率。

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

    所以我需要说的是“睡3000个,或者直到你收到一条消息,无论哪个先到。”我确信有一个API,但是我不确定它是什么。有人知道吗?

    7 回复  |  直到 14 年前
        1
  •  2
  •   Karel Petranek    14 年前

    使用计时器运行processfiles,而不是将其侵入主应用程序循环。然后,processfiles将按所需的间隔运行,消息将得到正确处理,而不会占用100%的CPU。

        2
  •  11
  •   Community CDub    8 年前

    这种东西很难纠正,所以我通常从msdn的api文档开始。

    这个 WaitForSingleObject 文件特别指向 MsgWaitForMultipleObjects 对于这些情况:

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

    msgwaitformultipleObjects(msgwaitformultipleObjects) 你有一个 dwWakeMask 参数,指定要返回的排队消息,以及描述可以使用的掩码的表。

    编辑 因为评论 Warren P :

    如果主循环由于 ReadFileEx , WriteFileEx QueueUserAPC ,然后您可以使用 SleepEx .

    ——杰罗恩

        3
  •  8
  •   Remy Lebeau    14 年前

    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;
    
        4
  •  1
  •   ErvinS    14 年前

    我在多线程应用程序中使用了一个带有奇怪结果的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 := TEvent.Create(nil, False, False, '');
    

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

    ExitEvent.SetEvent;
    

    我将此代码用于我的数据库连接池系统中的清理线程,但在您的情况下,它也应该工作得很好。

        5
  •  0
  •   Zippit    14 年前

    您不需要睡眠3整秒来保持低CPU使用率。即使是像sleep(500)这样的东西,也应该使您的使用率保持在很低的水平(如果没有等待处理的消息,那么它应该很快地通过循环并再次进入睡眠状态)。如果循环运行需要几毫秒,那么它仍然意味着线程大部分时间都在睡眠中。

    尽管如此,您的代码可能会从一些重构中受益。您说不希望processrequests阻止等待消息?循环中唯一的另一件事是processfiles。如果这取决于正在处理的消息,那么为什么它不能阻止呢?如果它不依赖于正在处理的消息,那么它能被拆分到另一个线程上吗?(前面关于如何通过计时器触发进程文件的建议是一个很好的建议)。

        6
  •  0
  •   Marco van de Voort    14 年前

    使用一个你在线程应该唤醒时发出信号的tevent。然后阻塞tevent(使用waitformultiple,如jeroen所说,如果您有多个事件需要等待)

        7
  •  0
  •   The_Fox    14 年前

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