如果我 运行 时间太长,我该如何终止自己?

How can i terminate myself if i run too long?

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

但应用程序挂起的几率约为 80,000 分之一(每两三个月)。根本原因是因为我们正在使用 Microsoft ServerXmlHttpRequest 组件来执行一些工作,并且 . The virtue of ServerXmlHttpRequest over XmlHttpRequest is that the latter is not recommended for important scenarios,例如可靠性和安全性很重要的地方(无人值守的服务器组件也是如此):

The ServerXMLHTTP object offers functionality similar to that of the XMLHTTP object. Unlike XMLHTTP, however, the ServerXMLHTTP object does not rely on the WinInet control for HTTP access to remote XML documents. ServerXMLHTTP uses a new HTTP client stack. Designed for server applications, this server-safe subset of WinInet offers the following advantages:

  • Reliability — The HTTP client stack offers longer uptimes. WinInet features that are not critical for server applications, such as URL caching, auto-discovery of proxy servers, HTTP/1.1 chunking, offline support, and support for Gopher and FTP protocols are not included in the new HTTP subset.
  • Security — The HTTP client stack does not allow a user-specific state to be shared with another user's session. ServerXMLHTTP provides support for client certificates.

作业正在 运行 作为计划任务。我需要定期继续 运行 的任务;如果现有进程已死,则将其杀死。

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

该方法的唯一缺点是它根本不起作用 - 它根本不会停止任务。挂起的进程保持运行ning.

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

职位

我尝试使用 Job Objects API:

A job object allows groups of processes to be managed as a unit. Job objects are namable, securable, sharable objects that control attributes of the processes associated with them. A job can enforce limits such as working set size, process priority, and end-of-job time limit on each process that is associated with the job.

那个音符听起来正是我所需要的:

A job can enforce limits such as end-of-job time limit on each process that is associated with the job.

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

PerProcessUserTimeLimit

If LimitFlags specifies JOB_OBJECT_LIMIT_PROCESS_TIME, this member is the per-process user-mode execution time limit, in 100-nanosecond ticks.

如果进程处于空闲状态(例如,像 ServerXmlHttpRequest 一样处于 MsgWaitForSingleObject 状态),那么它将不会累积 user 时间。我测试了它。我创建了一个时间限制为 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 秒后退出。这意味着持续时间是可配置的,或者可以在现场轻松地完全删除。

我使用了在命令行上向我的进程传递一个特殊的 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);
}