【问题标题】:How to implement a timed wait around a blocking call?如何围绕阻塞调用实现定时等待?
【发布时间】:2011-05-13 01:09:55
【问题描述】:

所以,情况是这样的。我有一个 C++ 库,它正在做一些进程间通信,它有一个 wait() 函数来阻止并等待传入​​的消息。困难在于我需要一个 timed 等待,如果在指定的时间内没有收到任何消息,它将返回一个状态值。

最优雅的解决方案可能是重写库以向其 API 添加定时等待,但为了这个问题,我假设它不可行。 (实际上看起来很难,所以我想知道另一个选项是什么。)

下面是我如何在繁忙的等待循环中使用伪代码:

while(message == false && current_time - start_time < timeout)
{
  if (Listener.new_message()) then message = true;
}

不过,我不想要占用处理器周期的繁忙等待。而且我也不想在循环中添加sleep() 调用以避免处理器负载,因为这意味着响应速度较慢。我想要一些通过适当的块和中断来做到这一点的东西。如果更好的解决方案涉及线程(这似乎很可能),我们已经在使用boost::thread,所以我更愿意使用它。

我发布这个问题是因为这似乎是一种具有明确“最佳实践”正确答案的情况,因为这是一种非常常见的模式。正确的做法是什么?

编辑添加:我担心的很大一部分是程序中的一个位置,它对性能至关重要,而且对于避免竞争条件或内存泄漏至关重要。因此,虽然“使用两个线程和一个计时器”是有用的建议,但我仍然试图弄清楚如何以安全和正确的方式实际实现它,而且我很容易看到自己在我的代码中犯了新手错误甚至不知道我做了。因此,一些实际的示例代码将不胜感激!

另外,我担心多线程解决方案:如果我使用“将阻塞调用放在第二个线程中并在该线程上执行定时等待”方法,如果被阻塞,第二个线程会发生什么电话永远不会返回?我知道第一个线程中的定时等待将返回,我会看到没有发生任何答案并继续处理,但是我是否“泄漏”了一个将永远处于阻塞状态的线程?有没有办法避免这种情况? (有什么方法可以避免这种情况并避免泄漏第二个线程的内存?)如果阻塞调用没有返回,我需要一个完整的解决方案来避免泄漏。

【问题讨论】:

    标签: c++ multithreading boost timeout boost-thread


    【解决方案1】:

    您可以使用sigaction(2)alarm(2),它们都是POSIX。您使用 sigaction 为超时设置回调操作,然后使用警报设置计时器,然后进行阻塞调用。如果阻塞调用没有在您选择的超时时间内完成(以秒为单位;如果您需要更精细的粒度,可以使用setitimer(2)),阻塞调用将被中断。

    请注意,C 语言中的信号有些复杂,并且对您在信号处理程序中可以执行的操作有相当严格的限制。

    此页面非常有用且相当简洁: http://www.gnu.org/s/libc/manual/html_node/Setting-an-Alarm.html

    【讨论】:

    • 谢谢!我认为这听起来像是我想要的——但我还没有足够的细节来理解如何去做。特别是,我不知道如何正确设置将中断阻塞调用并永久停止它而不是在中断处理程序退出后返回它的信号。你能补充一点关于如何做到这一点的解释吗?
    • 来自我回答中的 GNU 链接:“为了能够使用警报功能来中断可能会无限期阻塞的系统调用,重要的是在使用注册信号处理程序时不要设置 SA_RESTART 标志签名。”我会试试的。那么问题是库代码在其原始操作返回 EINTR 时会做什么。如果幸运的话,您正在使用的库将具有某种“停止”函数,您可以从信号处理程序中调用它,或者它只会从 EINTR 上的阻塞调用中返回。如果它更持久并且只是在 EINTR 上再次阻塞,那么生活会更艰难。
    • 好吧,这开始有道理了——我错过了那一行,也没有意识到 SA_RESTART 的存在,或者除了它从中断的地方继续之外,中断的代码可能会发生任何事情.感谢您指出了这一点! (我不确定库代码在这种情况下做了什么,或者我们可以在多大程度上依赖它,因为它有钩子让人们可以在该级别为不同的通信库添加不同的代码。但我会找出来的。)
    • 接受这个——我不认为这实际上是我想要的答案,但如果我想要的答案存在,我很确定这就是它。 (我认为我的特殊情况的实际答案是“没有答案;你不能用你所拥有的东西可靠地到达那里。”)谢谢!
    【解决方案2】:

    您想要的是 select(2) 之类的内容,具体取决于您所针对的操作系统。

    【讨论】:

    • 类似的,是的。不过,这似乎特定于文件描述符,而这是任意库调用。根据我使用的版本,它可能会或可能不会使用深层的文件描述符,但我无权访问它们。不过谢谢!
    【解决方案3】:

    听起来您需要一个“监视器”,能够通过共享互斥体(通常)向线程发送资源可用性信号。在 Boost.Thread 中,condition_variable 可以完成这项工作。

    【讨论】:

      【解决方案4】:

      您可能想查看timed locks:您的阻塞方法可以在开始等待之前获取锁,并在数据可用时立即释放它。然后,您可以尝试在您的定时等待方法中获取锁(超时)。

      【讨论】:

        【解决方案5】:

        将阻塞调用封装在单独的线程中。在该线程中有一个由条件变量保护的中间消息缓冲区(如前所述)。使您的主线程定时等待该条件变量。如果满足条件,则接收中间存储的消息。

        所以基本上在 API 和您的应用程序之间放置一个能够进行定时等待的新层。适配器模式。

        【讨论】:

        • 很好,但是如果阻塞调用永远不会解除阻塞,我的单独线程会发生什么?如果我在一个连续运行的程序中每分钟执行一次会怎样?
        【解决方案6】:

        关于

        如果被阻塞的调用永远不会返回,第二个线程会发生什么?

        我相信如果没有被调用函数(或库)的合作,没有什么可以干净地恢复。 “干净”意味着清理该线程拥有的所有资源,包括内存、其他线程、锁、文件、文件上的锁、套接字、GPU 资源……Un-干净地,你确实可以杀死线程失控。

        【讨论】:

          猜你喜欢
          • 2012-11-19
          • 1970-01-01
          • 1970-01-01
          • 2011-09-13
          • 2012-01-03
          • 1970-01-01
          • 1970-01-01
          • 2022-01-04
          • 1970-01-01
          相关资源
          最近更新 更多