即使出现未处理异常,也要保持应用程序运行

10

什么问题?
我正在开发一个需要全天候运行的控制台应用程序,无论何时都不能停止。有没有办法防止多线程应用程序因“某个线程中发生的未处理异常”而崩溃?

为什么?
请不要给出像“你应该管理好所有的异常”,“这永远不应该发生”等教训。我有我的原因:我们正在进行测试部署,需要保持此应用程序运行,记录异常,并重新启动所有线程。如果发生任何意外情况导致抛出未处理的异常,需要检测并调用一些方法来重新启动所有线程(由于层次设计,原子性是不可能实现的)

话虽如此,我知道如果已经因为未处理异常而崩溃,可能无法从“内部”重新启动应用程序(这已经被我实现了)。

到目前为止,我使用了Quartz.net的FileScan作业和Flag文件来检测此类问题,并从外部重新启动应用程序。但这听起来很卑劣。我想找到更清洁、不那么快速和肮脏的解决方案。

下降票/抨击警告 :我知道这可能不是“原本如此”可行的。请有创意/有帮助而不是突然批评,并将其视为一个“开放性问题”


3
所以...你为你的"公开问题"添加了一个"踩"警告,然后踩了每个试图帮助你的人。不错… - Ethan Cabiac
@EthanCabiac 对不起,你的回答并没有真正回答我之前评论的原因。在你编辑后就不同了。我会将其标记为答案,因为这是我自己发布的答案选择。让我们和平相处 :) - Mehdi LAMRANI
5个回答

12
如果要一直运行24/7,为什么不将其编写为Windows服务,并利用Windows内置的恢复选项呢?
此方法的额外优势是能够在计算机重启后继续运行,并将失败/重新启动记录在系统事件日志中。同时,使用Windows服务还能更加方便地进行配置。 配置恢复服务

我真的很想避免使用服务,并保持一切非操作系统绑定。 - Mehdi LAMRANI
1
你需要支持哪个操作系统?似乎用C#进行非Windows编程有些奇怪。 - JohnFx
1
@Mika:然后将非特定于操作系统的部分与特定于操作系统的部分分开。您可以编写多个对象或库。 :) - Robert P
2
@Mika:也许你不理解服务的意义。它是你编写的代码,用于执行需要一直运行的特定任务。当然,你自己也可以完成这个任务;C# 是图灵完备的,所以你需要的任何功能都是可能实现的。使用服务,你只需要将“当事情失败时重新启动”部分留给操作系统。微软编写了代码来为你完成这项工作。使用它是可以的。事实上,如果你使用操作系统来完成这项工作,会更好,因为微软已经聘请了高技能人员数十年来解决这个问题。他们已经为你解决了这个问题。利用它吧。 :) - Robert P
1
@MikaJacobi:Windows服务的支持是Windows的一部分。您可以控制旋钮的设置方式,但如果它对所有现有的Windows服务都足够好,那么它很可能也足够好用于您。 - John Saunders
显示剩余4条评论

9

您需要在当前应用程序域上附加一个事件处理程序来处理未处理的异常事件:

AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler

在处理程序内部,您需要找到一种方法将足够的状态(保存到文件、数据库等)保存下来,以便重启的应用程序能够将其传递给新线程。然后通过调用System.Diagnostics.Process.Start("MyConsoleApp.exe")实例化一个新的控制台应用程序。

非常小心地引入逻辑,以避免崩溃/重启/崩溃/重启的连续循环。


1
这已经实现了。这不会以任何方式帮助重新启动任何东西。应用程序将在处理程序完成其工作后退出。 - Mehdi LAMRANI
1
@Mika:如果你实现的异常处理程序没有重新启动你的程序,那不是异常处理程序的问题,而是你在UnhandledExceptionHandler中没有实现的行为。 - Robert P
@RobertP:这是非常重要的一点:异常处理程序确实按照指示执行,但是请纠正我如果我错了,应用程序在处理程序退出后立即终止。这证明了我的观点:它不允许应用程序保持活动状态。 - Mehdi LAMRANI
+1/标记为答案:我写完我的答案并发布后,编辑者也同时发布了完整的代码(见下文),基本上复制了我最终想出的相同思路。 - Mehdi LAMRANI
只是提醒一下:通过UnhandledExceptionHandler捕获错误将无法捕获例如在您P/Invoking的C++ DLL中调用abort()的情况。 - Dan Ambrisco
显示剩余2条评论

4
  • 无论如何,你都不能让一个进程持续运行。如果进程被杀死了怎么办?
  • 不想让一个进程无论如何都持续运行。如果进程状态被破坏以至于它继续运行会发生“坏事”,该怎么办?

2
我说过“请不要给出教训” :-) 我已经习惯了在SOF上得到这样的非回答,但它们总是让我感到不舒服。不要那么死板!通过“无论发生什么事情”,我的意思是“无论程序中出现什么问题”!此外,如果进程状态损坏,我将通过邮件收到通知并在服务器上看到它。此外,我有我的糟糕状态管理逻辑。但我需要它来保持东西运行,并在晚上重新启动,如果有什么问题。 - Mehdi LAMRANI
3
@mika:你请求帮助某件显然不理解的事情。如果你不想要一堂“课”,那就别请求帮助。 - Ethan Cabiac
1
由于某种原因,整篇文章都感觉像是雷蒙德·陈(Raymond Chen)博客文章的开头。 - JohnFx
@EthanCabiac:你显然将“跳脱常规思考”误认为是“缺乏理解”。此外,我还说过:“我知道这可能不可能”,但我非常好奇,从不满足于简单的拒绝。总有办法将事情推向比“一般习惯”更进一步的可能性。 - Mehdi LAMRANI
1
@MikaJacobi:我听到了你的话,但选择忽略。SO 不仅仅是为了回答你的问题,也是为了回答所有阅读它的人的问题。 - John Saunders
显示剩余4条评论

2
如果您使用的是 .Net 2.0 及以上版本,则答案是无法实现。

在 .NET Framework 1.0 和 1.1 版本中,发生在主应用程序线程之外的未处理异常会被运行时捕获,因此不会导致应用程序终止。因此,可能会引发 UnhandledException 事件而不终止应用程序。从 .NET Framework 2.0 开始,这种对子线程中未处理异常的后备机制被移除,因为这种静默失败的累积效应包括性能下降、数据损坏和死锁,所有这些都很难进行调试。有关更多信息,包括运行时不终止的情况列表,请参阅托管线程中的异常。

摘自此处:

http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx

如果你希望你的应用程序能够生存下来,那么你需要在你的方法周围使用非常积极的 try/catch 以确保没有任何异常逃脱。
我建议像其他人提到的那样使用 Windows 服务。它与控制台应用程序相同,但在顶部有一个额外的服务层代码。你可以轻松地将你的控制台应用程序转换为服务应用程序。只需要重写 service.start/pause/stop 方法即可。

感谢您明智的指导。确实非常有教益。 - Mehdi LAMRANI

2

我能想到以下解决方案有一些缺点,但对我来说现在足够好:

static void Main()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
        CS = new ConsoleServer();
        CS.Run();            
    }

    public static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception exception = (Exception)e.ExceptionObject;
        Logger.Log("UNHANDLED EXCEPTION : " + e.ExceptionObject.ToString());
        Process.Start(@"C:\xxxx\bin\x86\Release\MySelf.exe");
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接