如何处理不返回的函数
Posted
技术标签:
【中文标题】如何处理不返回的函数【英文标题】:How To Handle a Function That Doesn't Return 【发布时间】:2010-09-14 15:54:25 【问题描述】:我有一个名为 ApiCalls() 的函数,它被包裹在一个储物柜中,因为我使用的 api 不是多线程安全的。偶尔api调用无法返回,我想不出办法来处理这种情况。我正在考虑在锁对象上创建一个计时器,但似乎储物柜没有类似的东西。
【问题讨论】:
修复或替换 API 怎么样? 如果不能稳定,也可以将其移出进程。 这个 API 是非托管的,还是调用非托管的代码? 这是***.com/questions/2187086 的副本。您的问题本质上是“我有一个不可靠的子系统。我该怎么办?”,所以在这个疯狂的长答案结束时,另请参阅我的 cmets 处理不可靠的子系统:***.com/questions/2113261/…。这个答案启发了这篇关于同一主题的博文:blogs.msdn.com/b/ericlippert/archive/2010/02/22/… 【参考方案1】:这确实没有好的答案。一个不好但可能可行的答案是有一个看门狗线程,它在超时后中止调用线程。换句话说,在获取锁之后但在调用 API 之前,你会命令看门狗杀死你。当您从通话中回来时(如果您回来了),您将取消看门狗。
同样,这不是一个很好的解决方案,因为 Abort 非常混乱。
【讨论】:
+1 - 可能是这里唯一可行的解决方案。记住要捕获生成的 Abort 异常。虽然不太确定您将如何处理任何内存泄漏,但在这种情况下,除非经常且经常执行,否则它们可能不会造成太大问题。 问题不在于泄漏,而在于缺少清理。您可以捕获异常,这样做很好,但不会阻止它传播。【参考方案2】:我认为您无法合理地从这个问题中恢复过来。假设您可以超时,然后您将尝试再次调用 API,但之前的调用仍然处于活动状态,并且您说 API 不是线程安全的。
你根本无法保护自己免受这种根本有缺陷的依赖。
唯一真正安全的做法是重新启动进程。 Steven Sudit 的建议是实现这一目标的一种方法。
【讨论】:
对,看门狗的概念可以扩展到重新启动整个过程,这根本不是一个坏主意。【参考方案3】:这可以通过将 API 调用包装在单独的程序集中并使用 AppDomain 类将该程序集加载到单独的 application domain 中来解决.....
使用应用程序域进行隔离 可能导致进程中断的任务。 如果 AppDomain 的状态是 执行任务变得不稳定, AppDomain 可以不卸载 影响进程。这是 当一个进程必须运行时很重要 长时间不重启。
然后您可以在单独的 AppDomain 中调用线程中止,向主机域发出中止发生的信号。主机域将卸载有问题的域,从而卸载 API,并使用 API 重置启动一个新域。您还需要在 API 域上设置一个看门狗,以便在 API 域冻结时主机可以采取行动。
其他链接:C# Nutshell AppDomain Listings、cbrumme's WebLog、Good example of use of AppDomain、Using AppDomain to Load and Unload Dynamic Assemblies
【讨论】:
我同意线程不能提供足够的绝缘,这就是为什么我赞成 djna。但是,我也不确定 AppDomain 是否可以。这在很大程度上取决于 API 调用不返回的为什么。如果它正在破坏内存,那么 AppDomain 不会阻止它,只有进程会。 @Steven 是的...如果 API 不受管理,它在技术上可以做任何事情,但 AppDomain 确实提供了大量的保护...MSDN:“您可以在单个应用程序域中运行多个应用程序域进程具有与单独进程中存在的相同级别的隔离,但不会产生进行跨进程调用或在进程之间切换的额外开销。” 是的,我也读过,但它是基于这样的假设,即程序集不会覆盖内存。在这种情况下,这不是一个安全的假设。 @Steven 同意。对于可能随机损坏其域或进程之外的内存的程序集,您无能为力。 啊,但这就是重点:它不能破坏域外的内存!【参考方案4】:唯一安全的解决方案可能是启动另一个进程来处理 API 调用,然后在它们卡住时终止该进程。即使这样也不能保证 API 的处理程序不会进入只能通过系统重新启动才能治愈的虚假状态,但是使用 Thread.Abort 可以致命地伤害进程。
如果您不想使用“不受信任”的方式杀死进程,您可以让进程中的一个线程执行 API 调用,而另一个线程则监视“请死”消息。看门狗可能很棘手。如果看门狗设置为 15 秒,而一个动作需要 17 秒才能完成,则可能会请求一个动作,15 秒后超时,重试该动作,15 秒后超时,等等。每次失败后调整看门狗时间可能会很好(例如,尝试一个动作,让它最多有 15 秒;如果这不起作用,并且没有人抱怨,请再试一次,让它走 30 秒;如果仍然如此不好,给它60秒。)
【讨论】:
对了,有办法取消的话,需要确保限制重试次数。 但是,“不受信任”的进程关闭应该没问题。没有必要让它复杂化。以上是关于如何处理不返回的函数的主要内容,如果未能解决你的问题,请参考以下文章
如何处理不抛出 catch 的 API 请求? (403 错误)