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