我正在开发一个大型应用程序,它包含许多较小的插件/应用程序.
它们不够大,不足以成为一个完整的进程,但是在一个进程中,它太小而无法在一个线程中运行,而我希望它基于一个插件基础.如果该插件的较新版本可用,则应该卸载,更新并重新启动.
在我寻找解决方案期间,我可以使用魔术字AppDomain,我引用:
"使用应用程序域来隔离可能导致进程失效的任务.如果正在执行任务的AppDomain状态变得不稳定,则可以卸载AppDomain而不会影响进程.当进程必须长时间运行而不重新启动时,这很重要您还可以使用应用程序域来隔离不应共享数据的任务."
因此,这正是我想要的.但是,我猜他们的"状态变得不稳定"与我的观点不同.我正在考虑一个问题,其中一个插件抛出异常,无论出于何种原因.我想抓住,通过电子邮件发送,卸载并重启(如果可能的话).
所以我创建了一个启动的应用程序,查找其文件夹中的所有.dll文件.检查dll是否包含插件.为该插件创建一个新的AppDomain,一旦加载了所有内容,它将启动每个插件.(每个插件可以由多个线程组成,与其他插件紧密共存).
所以我还在那里添加了一个超时,在5秒之后触发抛出一个新的Exception(); 在AppDomain上添加了UnhandledException事件来处理它.但是,它抓住了它,并且在导航之后,仍然"崩溃"了整个过程,包括所有额外的孩子 - AppDomains.
但它在报价中明确指出"隔离"可能"打倒流程"的任务.我错过了一些重要的东西吗?我对报价的看法是错误的吗?
Dmi*_*try 16
由于.NET 2.0 未处理的异常导致进程崩溃.从AppDomain.UnhandledException事件文档:
此事件提供未捕获异常的通知.它允许应用程序在系统默认处理程序向用户报告异常并终止应用程序之前记录有关异常的信息.
这同样适用于AppDomain.FirstChanceException:
此事件仅是通知.处理此事件不会以任何方式处理异常或影响后续异常处理.
您需要考虑如何处理异常,就像在普通应用程序中执行异常一样.仅使用AppDomains无济于事.如果在给定的AppDomain中没有处理异常,它将在调用AppDomain时重新抛出,直到它被处理或崩溃进程.处理一些异常非常好,不要让它们崩溃你的过程.
AppDomain是程序集和内存的逻辑容器(不适用于线程).AppDomain的隔离意味着:
域B中创建的对象不能由域B直接访问(没有编组).这允许在不影响域B中的任何内容的情况下卸载域A.这些对象将在"拥有"域被卸载时自动删除.
可以使用AppDomain自动卸载程序集.这是您从进程中卸载托管dll的唯一方法.这对DLL热交换很有用.
AppDomain安全权限和配置可以与其他AppDomain隔离.加载不受信任的第三方代码时,这可能会有所帮助.它还允许您覆盖程序集的加载方式(版本绑定,卷影复制等).
使用AppDomain的最常见原因是您运行不受信任的第三方代码.或者你有非托管代码,想要托管CLR或需要dll热交换.我认为在CLR托管方案中,当第三方代码抛出未处理的异常时,您可以防止进程崩溃.
此外,您可能希望查看System.Addin或MEF,而不是滚动自己的基础结构.
未处理的异常有两个问题.AppDomain只解决其中一个问题.你正试图处理另一个.
好消息首先.处理异常时,必须恢复程序状态,就好像异常从未发生过一样.一切都必须重新回到代码运行前的状态.你通常有一堆catch和finally子句来撤消代码执行的状态变化.当然没有什么比这更简单了.但如果异常未得到处理,则完全不可能.你不知道究竟是什么变异以及如何恢复它.AppDomain使用aplomb处理这个非常棘手的问题.你卸下它,剩下的状态就消失了.不再有垃圾收集堆,没有更多的加载器堆(静态).在创建AppDomain 之前,整个enchilada会重置为状态.
那很棒.但是还有一个问题很难处理.您的程序被要求执行一项工作.该线程开始做这项工作.但它遭受了心脏病发作.第一个大问题:线程已经死了.如果你的程序只有一个线程可以开始,这是个坏消息.没有线程,程序终止.很高兴AppDomain首先卸载,但它确实没有任何区别,它无论如何都会被卸载.
也是一个大问题:完成这项工作确实非常重要.它没有.重要的是,这项工作是平衡公司损益表.这并没有得到完成,有人将不得不因为采取照顾不均衡的说法是会得到很多人很不高兴.
你是怎么解决的?
只有少数选定的场景可以接受.服务器场景.有人要求它做某事,服务器报告"无法做到,请联系系统管理员".ASP.NET和SQL Server的工作方式.他们使用AppDomains来保持服务器稳定.并让系统管理员来处理问题.您必须创建这种支持系统才能使AppDomains为您服务.
只是为使用应用程序域考虑(自己在那里)的任何人添加一些关于主题的额外信息,主要是为了保证应用程序的稳定性:
几年前,该System.AddIn团队发布了一篇非常有趣的博客文章.使用AppDomain隔离来检测加载项失败.
它解释了只有进程外加载项才能保证主机的稳定性.进一步来说:
从子线程上的CLR v2.0未处理的异常开始,现在将导致整个进程被拆除,因此主机无法从中完全恢复.
因此,他们建议订阅AppDomain.UnhandledException,并在应用程序崩溃之前,存储有关导致此异常的人的信息(日志,数据库等).然后,下次应用程序启动时,请使用此信息来保护您的应用程序.也许您没有加载加载项,或者您通知用户并让他/她决定.(Microsoft Office应用程序遵循此方法并禁用了导致主机崩溃的插件.然后您必须自己重新启用它们.)
他们还发布了另一篇博客文章,展示了即使在主机在另一台主机(IIS,WAS等)中运行的情况下如何执行此操作. 有关从托管加载项记录UnhandledExeptions的更多信息.
虽然这两篇文章都围绕着System.AddIn它们,但它们包含了任何试图提高其插件感知应用程序稳定性的人的有用信息.