【问题标题】:Loop without consuming too many CPU cycles and without sleep()?循环而不消耗太多CPU周期并且没有sleep()?
【发布时间】:2011-06-06 15:16:35
【问题描述】:

我正在做一个 VoIP 程序,它不断检查音频记录缓冲区中是否有任何内容(FMOD 库,只要函数 getRecordPosition > 0,那么缓冲区中就有数据)。

所以应该是这样的:

while (true) {
    if(getRecordPosition>0) {
     process data....
    }
}

但是,这会导致非常高的 CPU 使用率。一个版本是使用 sleep() 但如果可能的话我宁愿不使用它。 例如,带有事件驱动循环的 win32 消息处理不会消耗很多 cpu 周期,这是我试图模仿的东西。同时我明白函数 getRecordPosition() 必须经常调用,看看返回值是否高于 0。

为了保持较低的 CPU 使用率,我是否坚持执行 while(true) 循环和 sleep() 一小段时间?

我已经用谷歌搜索并进行了一些查找,但大多数返回是使用 sleep() 或与 mutex 进行的一些 POSIX 同步。 (我正在做一个 c++ win32 应用程序)

干杯

---编辑:忘了说我无权访问 fmod 源代码:/ ---

【问题讨论】:

  • 你控制录音缓冲区吗?
  • 你有控制数据写入缓冲区的点吗?
  • 如果您提到不想睡觉的原因,也许会有所帮助。调用 Sleep 是执行此操作的正常方法,因为它释放线程的时间片并允许其他线程/进程运行。
  • 睡眠是浪费,就像旋转是浪费一样,只是稍微少一些。此外,它是非确定性的。
  • 如果你想要一个事件驱动的系统,你需要 FMOD 在缓冲区准备好时提醒你的进程或线程。 getRecordPosition() 是如何工作的?

标签: c++ multithreading winapi synchronization cpu-usage


【解决方案1】:

如果可以的话,最好的做法是不要像您所说的那样模拟事件驱动的架构,而是实际使用事件驱动的架构。我对您的代码一无所知,尤其是您是否控制着录音缓冲区的代码。但是如果您确实控制了写入缓冲区的代码,那么您可以在写入缓冲区时触发一个事件:

(后面是伪命题)

主线程:

HANDLE buf_event = CreateEvent(...);
// ...
CreateThread(BufferControl, ...);

缓冲区写入线程:

OnWriteToBuffer()
{
  buffer.Write(...);
  SetEvent(buf_event);
}

然后在你想要做某事的线程中,当缓冲区中有数据等待时,等待事件发出信号:

缓冲区读取线程

rc = WaitForSingleObject(buf_event, INFINITE);
if( rc == WAIT_OBJECT_0 )
{
  // there's somethign in the buffer
}

【讨论】:

  • 好建议,不幸的是我无法访问源代码:/
【解决方案2】:

您可以使用“SwitchToThread”将处理器让给另一个线程,并检查返回值。如果是真的,你屈服了,如果不是,没有其他线程需要运行。如果我没记错的话,FMOD 运行一个线程,所以你可能会屈服于那个线程。

如果它无法让出或继续占用大量 CPU 时间,您可以使用让出和休眠的某种组合。

【讨论】:

  • 据我所知SwitchToThreadSleep(0) 相同
  • 在 Windows 中,是的,SwitchToThread 是 Sleep(0),但 SwitchToThread 也返回一个 BOOL,表示切换是否发生。在某些情况下可能很高兴知道。
【解决方案3】:

我不熟悉 FMOD 库,但它是否提供了数据放入缓冲区时的通知回调?如果不是这样,您几乎会在使用某种形式的睡眠时陷入困境(您可以将该代码放在一个线程中并使用 nanosleep 之类的东西来保持良好的响应时间)。

【讨论】:

    【解决方案4】:

    我们遇到了类似的问题。
    我们有许多进程试图共享资源。在其中一个持有该资源之后,另一个应该等待该资源可用。这意味着,请稍等片刻,然后尝试锁定资源,如果不可用,则再次休眠。
    由于我们必须在 Windows 和 Linux 上开发该代码,我们面临不同的解决方案,即使使用 boost(但没有有效的结果)。 无论如何,Windows 中最好的解决方案是在资源上使用 WaitForSingleObject(),但在 linux 上我们只需要休眠 1 毫秒。该解决方案似乎消耗了非常低的 CPU - 而不是一段时间(true) - 授予资源以尽快获得。

    一开始,我们在资源可用性测试和随后的测试之间失去了很多睡眠时间,因为我们睡眠了很多毫秒。现在我们实际上更快了。 高温

    【讨论】:

    • “我们只需要睡 1 毫秒”——你可能认为是 1 毫秒,但实际上它至少是一个时间片,可能长了 10 倍。
    • 对。 Sleep() 的精确度以毫秒为单位,但它的精确度是臭名昭著的差。你实际上可能睡了大约 40 毫秒。
    • 无论软件中断对于应用程序运行的平台而言,无论多长时间,您都将处于休眠状态。在 Windows 上,是的,大约 40 毫秒。我相信 Linux 中断更频繁,所以它会等待更短的时间。
    【解决方案5】:

    考虑 Win32 必须提供的其他同步原语。自动重置事件听起来像您需要的。它们也可以跨进程工作。

    【讨论】:

      【解决方案6】:

      为了避免消耗CPU,避免Sleep()函数效率低下,需要避免轮询,而是使用事件驱动机制。有现成的框架。例如,在 GBL 中,您可以定义一个信号,例如 getRecordPosition,并将其绑定到处理程序:

      Handler([](){ do_something(); }) << getRecordPosition;
      

      或者如果你需要一个单独的线程,你会等待信号:

      CreateThread([](){ Wait(getRecordPosition); do_something(); });
      

      【讨论】:

        猜你喜欢
        • 2021-04-14
        • 2012-07-20
        • 2012-09-25
        • 1970-01-01
        • 2021-07-09
        • 1970-01-01
        • 2012-10-25
        • 2017-10-02
        • 1970-01-01
        相关资源
        最近更新 更多