【问题标题】:C++: How to implement a timeout for an arbitrary function call?C++:如何为任意函数调用实现超时?
【发布时间】:2010-10-27 03:57:57
【问题描述】:

不幸的是,我需要调用一个有时不会在给定时间内终止的库函数。有没有办法调用该函数但如果它没有在n 秒内终止则中止它?

我不能修改函数,所以我不能直接把中止条件放进去。我必须在外部为函数添加超时

将它作为(增强)线程启动是否可能是一种可能的解决方案,然后我可以在一段时间后终止它?这样的东西会起作用吗?我实际上相信该函数是 not 线程安全的,但是如果我将它作为 only 单线程运行它也没关系,对吧?还有其他(更好的)解决方案吗?

【问题讨论】:

  • 库函数有什么作用?
  • 呃,计算一些东西......在某些条件下实际上不会终止的科学计算。

标签: c++ function timeout abort


【解决方案1】:

您可以生成 boost::thread 来调用 API:

boost::thread api_caller(::api_function, arg1, arg2);
if (api_caller.timed_join(boost::posix_time::milliseconds(500)))
{
    // API call returned within 500ms
}
else
{
    // API call timed out
}

不过,Boost 不允许您终止工作线程。在本例中,它只是孤立的。

您必须小心该 API 调用的作用,因为它可能永远不会释放它获取的资源。

【讨论】:

  • 如果 api_function 返回一个值,我该如何访问它?
  • 此方法创建一个直接调用 API 的简单函子。要捕获结果,您必须编写自己的函子或 lambda 以将 API 的返回代码分配给可以从其他地方访问的某个变量。在 C++0x 中,您可以编写 RETTYPE retcode; boost::thread api_caller( [&retcode] (type1 arg1, type2 arg2) { retcode = ::api_function(arg1, arg2); });
【解决方案2】:

我认为实现此目的唯一安全的方法是生成一个单独的沙盒 进程,该进程调用库函数作为应用程序的代理。您需要在应用程序和代理之间实现某种类型的 IPC。在读取 IPC 回复时实现超时是相当简单的。如果由于超时而导致读取失败,您可以安全地终止代理,而不会危及应用程序的健康。

【讨论】:

    【解决方案3】:

    您所说的通常称为“看门狗”系统。看门狗通常是检查所有其他线程状态的第二个线程。看门狗通常设置为定期运行。如果没有收到来自其他线程的响应,则看门狗可以通知用户,甚至在可能安全的情况下杀死有问题的线程(取决于您的应用程序)。

    【讨论】:

    • 根据库函数的作用,杀死无响应的线程可能非常不安全。一种安全的方法是将任务分派给单独的进程。
    • 如何启动这样一个单独的进程?我的应用程序可以在不通过文件系统的情况下传递一些数据(并返回一些结果数据)吗?
    • 这有点复杂,并且特定于平台 - 我认为很容易成为一个单独的 stackoverflow 问题。有几种方法可以在进程之间传递数据。这称为 IPC(进程间通信),也是特定于平台的。
    • 这个实现为我解决了上述问题:comments.gmane.org/gmane.comp.lib.boost.user/59477
    【解决方案4】:

    线程的问题是线程终止后您将无法释放某些资源。如果您没有获得必须释放的资源,请使用线程。

    【讨论】:

    • 请注意,有些资源也可能在您的控制范围之外被获取 - 您可以调用获取锁的 API 等等,终止线程将导致这些锁成为孤立对象。
    【解决方案5】:

    【讨论】:

    • +1,有趣的想法。我看到 timeout 参数还不适用于 Windows。此外,函数使用的任何资源仍将处于不确定状态(至少未分配,并且可能处于无效的中间状态),因此不要尝试多次运行函数或取决于它所涉及的任何内容。
    • 这个问题有一个很好的例子:this one
    【解决方案6】:

    问题在于,如果使用进程内解决方案没有函数的支持,您最终可能会处于无效状态。

    示例:当您在内存分配过程中终止线程时,您的进程堆可能已损坏。

    因此,您可能会终止呼叫,但您也必须终止进程。在许多情况下,破坏性副作用的可能性很小,但我不会将我的计算押在这一点上。

    正如 Ben Straub 建议的那样,您可以将线程孤立起来:将其置于最低优先级并让它无限运行。这当然只是一个有限的解决方案:如果线程消耗资源(可能),它们会减慢系统速度,每个进程的线程也有限制(通常是由于线程堆栈的地址空间)。

    一般来说,我更喜欢外部流程解决方案。一个简单的模式是这样的:
    将输入数据写入文件,以文件作为参数启动外部进程。外部进程将进度(如果有的话)写入可以监控的磁盘文件,甚至可以允许进程从它开始的地方恢复。结果写入磁盘,父进程可以读入。

    当您终止进程时,您仍然需要处理对外部资源(如文件)的同步访问,以及如何处理废弃的 mutice、半写文件等。但这通常是获得稳健解决方案的方法。

    【讨论】:

      【解决方案7】:

      “不幸的是,我需要调用一个有时不会在给定时间内终止的库函数。有没有办法调用该函数但如果它没有在 n 秒内终止则中止它?”

      简短的回答是否定的。这通常很麻烦...调用本身必须在某个时间终止(实现自己的超时),但阻塞调用通常很麻烦(例如 gethostbyname()),因为这取决于它们(或系统)的超时,而不是你的。

      因此,只要有可能,就尽量让线程中运行的代码在必要时干净地退出——代码本身必须检测并处理错误。它可以发送消息和/或设置状态,以便主(或其他)线程知道发生了什么。

      个人偏好,在高可用性系统中,我喜欢我的线程经常旋转(尽管没有忙锁定),有特定的超时,调用非阻塞函数,并且有精确的退出条件。全局或线程特定的“完成”变量可以实现干净退出。

      【讨论】:

        【解决方案8】:

        你需要的是一个线程和一个Future Object,它可以保存函数调用的结果。

        有关使用 boost 的示例,请参阅here

        您需要在超时后检查未来,如果未设置,请采取相应措施。

        【讨论】:

        • Futures 很酷而且通常很有用,但我认为它们不能解决 OP 提出的问题。如果函数运行时间过长,他想停止函数运行
        • 好吧,如果超时到期,他可以尝试终止线程,但正如许多其他人指出的那样,这是不安全的。如果函数稍后终止(除非它被破坏),那么未来将允许他在超时后继续他的主程序,并简单地忽略线程和未来,因为它知道它将在未来的某个时间终止。
        【解决方案9】:

        使用孤立进程,启动它并计时它的执行时间。如果超时,调用操作系统将其杀死。

        如何避免比赛条件。关于这个模式:

        • 创建一个文件以存储在 args 中(当然,所有内容都作为 VAL 传递)。 孤儿进程只允许从此文件中读取数据。

        • 孤儿处理输入数据,创建带有结果值的输出文件并关闭它。

        • 只有当一切都完成后,orphan 才会删除输入文件,这一事实向主进程发出信号表明工作已完成。

        这避免了读取半写文件的问题,因为master首先注意到输入文件的缺失,打开读取输出文件,这肯定是完成的(因为在删除输入之前关闭,并且操作系统调用堆栈是顺序的)。

        【讨论】:

          猜你喜欢
          • 2014-05-23
          • 1970-01-01
          • 2017-03-25
          • 2011-02-24
          • 1970-01-01
          • 1970-01-01
          • 2016-11-16
          • 2018-11-14
          相关资源
          最近更新 更多