【问题标题】:AppDomain, handling the exceptionsAppDomain,处理异常
【发布时间】:2011-08-15 23:25:15
【问题描述】:

我正在开发一个由许多较小的插件/应用程序组成的大型应用程序。

它们不足以成为一个完整的进程,但太小而无法在一个线程中运行,在一个进程下,以及我希望基于插件的基础上拥有它。 如果该插件的更新版本可用,则应将其卸载、更新并重新启动。

在我寻找解决方案的过程中,我可以使用魔法词 AppDomain,并引用:

“使用应用程序域来隔离可能导致 过程。如果正在执行任务的 AppDomain 的状态变为 不稳定,可以卸载AppDomain而不影响进程。 当一个进程必须长时间运行时,这一点很重要 重新启动。您还可以使用应用程序域来隔离任务 不应共享数据。”

因此,这正是我想要的。但是,我猜他们的“状态变得不稳定”与我的观点不同。我正在考虑一个问题,其中一个插件出于某种原因引发异常。我希望它被捕获、通过电子邮件发送、卸载并重新启动(如果可能的话)。

所以我创建了一个启动的应用程序,它会在其文件夹中查找所有 .dll 文件。检查 dll 是否包含插件。为该插件创建一个新的 AppDomain,一旦加载了所有内容,它将启动每个插件。 (每个插件可以由多个线程组成,彼此愉快地共存)。

所以我还在那里添加了一个超时,它会在 5 秒后触发以抛出一个新的 Exception();在 AppDomain 上添加了一个 UnhandledException 事件来处理它。但是,它捕捉到了它,并且在捕捉之后,仍然“崩溃”了整个过程,包括所有额外的子 AppDomain。

但它在引文中明确指出“隔离“可能”导致流程中断的任务”。所以我错过了一些重要的东西吗?我对报价的看法是错误的吗?

【问题讨论】:

  • 你看过MEF吗?
  • @Inuyasha 不,我没有,明天我回去工作。
  • 你是如何“启动每个插件”的?如果您使用了其中一种创建实例和展开方法,则异常将返回到您需要捕获它的调用域。
  • “太小而不能在线程中运行” 为什么组件的大小会影响这一点?如果它需要多个线程,你可以这样做,不需要为此使用 AppDomains。 (您似乎还有其他使用它们的原因,但我不认为这是一个原因。)
  • @Inuyasha 我只是看了 MEF 和一些例子,它看起来不像我可以用于这个项目的东西。由于每个插件都侦听自己的 Socket 并通过套接字而不是通过“主机”等待数据,因此这里对我们的项目进行更准确的描述:stackoverflow.com/questions/7071995/…(@编辑)

标签: c# .net


【解决方案1】:

自 .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,而不是滚动您自己的基础架构。

【讨论】:

【解决方案2】:

未处理的异常存在两个问题。 AppDomain 只解决其中一个问题。你正在尝试处理另一个。

首先是好消息。当您处理异常时,您必须恢复程序状态,就好像该异常从未发生过一样。一切都必须回到代码运行之前的状态。您通常有一堆 catch 和 finally 子句来撤消代码执行的状态突变。当然没有什么很简单的。但如果异常未处理,则完全不可能。您完全不知道 什么 发生了变异以及如何恢复它。 AppDomain 沉着应对这个非常棘手的问题。你卸载它,剩下的任何状态都消失了。没有更多的垃圾收集堆,没有更多的加载器堆(静态)。整个 enchilada 将重置为您创建 AppDomain之前的任何状态。

那太好了。但是还有另一个问题也很难处理。您的程序被要求执行一项工作。线程开始做这项工作。但它心脏病发作了。大问题一:线程死了。如果您的程序开始时只有一个线程,那将是一个非常糟糕的消息。没有线程了,程序终止。很高兴 AppDomain 首先卸载,但它真的没有任何区别,反正它会被卸载。

也是个大问题:完成这项工作真的很重要。它没有。重要的是,比如说,这项工作是平衡公司损益表。这没有完成,有人 将不得不处理这个问题,因为平衡声明会让很多人非常沮丧。

你是怎么解决的?

只有少数选定的场景可以接受。服务器场景。有人要求它做某事,服务器报告“不能做,请联系系统管理员”。 ASP.NET 和 SQL Server 的工作方式。他们使用 AppDomains 来保持服务器稳定。并有系统管理员来处理问题。您必须创建这种支持系统才能使 AppDomains 为您服务。

【讨论】:

  • 另外需要注意的是,如果线程在新的 appdomain 中产生了一个新的子线程,并且如果这个新线程抛出异常,则无法恢复。该过程将停止(尽管您可能仍会收到该事件)。因此,在这种情况下,即使使用了 try catch,插件仍然可以降低进程。完整的证明解决方案是将其托管在一个新进程中,但这当然有其自身的缺点。
【解决方案3】:

只需为考虑(我自己去过那里)主要使用应用程序域来保证应用程序稳定性的任何人添加一些关于该主题的额外信息:

几年前,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 为中心,但它们包含的信息对于任何试图提高其插件感知应用程序稳定性的人都很有用。

【讨论】:

    【解决方案4】:

    AppDomain 更常用于卸载程序集(如您的建议)和控制 .NET 访问级别、配置等启动参数。如果您真的想要“隔离”,那么最好的选择始终是工作进程;但是,工作量要大得多。

    我在几个项目中做了很多这样的事情。只是为了给出一个广泛的画面,我们在大多数通信中使用Google ProtoBuffers (Jon Skeet's port) 而不是managed Windows LRPC Library。对于工作进程管理,我们严重依赖命名事件,我最近发布了一个 inter-process event library here 就是为了这个目的。

    【讨论】:

    • “工作进程”是什么意思?哪个 C# 类?线程、任务还是 BackgroundWorker?
    • 他的意思是完全不同的 Windows 进程。就像 MyApp.exe 的新实例一样。
    • 分离过程是最安全的。这样可以保证插件不会降低您的流程,因为即使在单独的应用程序域中使用插件它仍然可以降低您的流程
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多