如果我跑得太久,我怎么能终止自己呢?

时间:2016-05-05 15:44:04

标签: windows winapi process watchdog reliability

我有一个定期运行的应用程序(这是一个计划任务)。该任务每分钟启动一次,通常只需几秒钟即可完成业务,然后退出。

但是,应用程序将挂起的机会(每两到三个月)有大约80%的机会。根本原因是因为我们使用Microsoft ServerXmlHttpRequest组件来执行某些工作,sometimes it just decides to hang服务器 XmlHttpRequest优于XmlHttpRequest的优点是后者是not recommended for important scenarios,例如可靠性和安全性很重要(无人值守服务器组件也是如此):

  

ServerXMLHTTP对象提供与XMLHTTP对象类似的功能。但是,与XMLHTTP不同,ServerXMLHTTP对象不依赖于WinInet控件来访问远程XML文档。 ServerXMLHTTP使用新的HTTP客户端堆栈。这个服务器安全的WinInet子集专为服务器应用程序而设计,具有以下优势:

     
      
  • 可靠性 - HTTP客户端堆栈提供更长的正常运行时间。对于服务器应用程序并不重要的WinInet功能,例如URL缓存,代理服务器的自动发现,HTTP / 1.1分块,离线支持以及对Gopher和FTP协议的支持都不包含在新的HTTP子集中。
  •   
  • 安全性 - HTTP客户端堆栈不允许与其他用户的会话共享特定于用户的状态。 ServerXMLHTTP提供对客户端证书的支持。
  •   

作业正在作为计划任务运行。我需要继续定期运行任务;如果它已经死了就会杀死它。

Windows任务计划程序确实有一个强制关闭运行时间太长的任务的选项:

enter image description here

这种方法的唯一缺点是它根本不起作用 - 它根本不会停止任务。挂起的进程一直在运行。

鉴于我不能相信Microsoft ServerXmlHttpRequest不能任意锁定,并且任务调度程序无法终止计划任务,我需要一些方法来自己完成。

工作

我尝试使用Job Objects API

  

作业对象允许将进程组作为一个单元进行管理。作业对象是可命名的,安全的,可共享的对象,用于控制与其关联的进程的属性。作业可以在与作业关联的每个进程上强制执行限制,例如工作集大小,进程优先级和作业结束时间限制

那一个音符听起来就像我需要的那样:

  

作业可以对与作业关联的每个进程强制执行作业时间限制等限制。

该方法的唯一缺点是它不起作用。工作不能对流程施加时间限制。他们只能强加user time limit on a process

  

PerProcessUserTimeLimit

     

如果LimitFlags指定JOB_OBJECT_LIMIT_PROCESS_TIME,则此成员是每进程用户模式执行时间限制,以100纳秒为单位。

如果进程处于空闲状态(例如,作为ServerXmlHttpRequest坐在MsgWaitForSingleObject处),那么它将不会累积用户时间。我测试了它。我创建了一个时间限制为1秒的作业,并将 self 进程放入其中。只要我不在我的测试应用程序周围移动鼠标,它就可以在那里停留超过一秒钟。

看门狗线程

我能想象的唯一其他技术,因为我的主线程无限期被阻止,是另一个线程。我可以想象的唯一解决方案是生成另一个将在我的三分钟内休眠的线程,然后ExitProcess

Int32 watchdogTimeoutSeconds = FindCmdLineSwitch("watchdog", 0);
if (watchdogTimeoutSeconds > 0)
    Thread thread = new Thread(KillMeCallback, new IntPtr(watchdogTimeoutSeconds));

void KillMeCallback(IntPtr data)
{
   Int32 secondsUntilProcessIsExited = data.ToInt32();
   if (secondsUntilProcessIsExited <= 0) 
      return;

   Sleep(secondsUntilProcessIsExited*1000); //seconds --> milliseconds

   LogToEventLog(ExtractFilename(Application.ExeName), 
         "Watchdog fired after "+secondsUntilProcessIsExited.ToString()+" seconds. Process will be forcibly exited.", EVENTLOG_WARNING_TYPE, 999);

   ExitProcess(999);
}

这很有效。唯一的缺点是it's a bad idea

有人能想到更好的东西吗?

修改

现在我将实施

Contoso.exe /watchdog 180

因此该过程将在180秒后退出。这意味着持续时间是可配置的,或者可以在现场轻松删除。

1 个答案:

答案 0 :(得分:0)

我使用了在命令行上将特殊的 WatchDog 参数传递给我的进程的路径;

>Contoso.exe /watchdog 180

在初始化期间,我检查是否存在WatchDog选项,其后面是整数秒:

String s = Toolkit.FindCmdLineOption("watchdog", ["/", "-"]);
if (s <> "")
{
   Int32 seconds = StrToIntDef(s, 0);
   if (seconds > 0)
      RunInThread(WatchdogThreadProc, Pointer(seconds));
}

和我的线程程序:

void WatchdogProc(Pointer Data);
{
   Int32 secondsUntilProcessIsExited = Int32(Data);

   if (secondsUntilProcessIsExited <= 0)
      return;

   Sleep(secondsUntilProcessIsExited*1000); //seconds -> milliseconds

   LogToEventLog(ExtractFileName(ParamStr(0)), 
         Format("Watchdog fired after %d seconds. Process will be forcibly exited.", secondsUntilProcessIsExited), 
         EVENTLOG_WARNING_TYPE, 999);

   ExitProcess(2);
}