【问题标题】:How to avoid busy looping in specific case如何避免在特定情况下忙循环
【发布时间】:2012-06-12 06:57:55
【问题描述】:

我有一个问题,我真的不知道如何解决。我有一个多路复用多个连接的程序。这些连接同时接收流数据。我必须配置非阻塞套接字,因为流具有不同的比特率。现在我实际上所做的是将这些套接字保存在一个数组中循环遍历它们,并使用 select 检测是否有数据要读取,如果没有则继续处理数组中的下一个元素。 它工作得很好,除了 CPU 总是 100% 的事实。实际上,如果在某个时候没有任何内容可以从任何套接字读取,它仍然会循环。我真的不知道如何在任何套接字上没有可用数据时阻塞循环,并在有数据时继续运行。我认为这可能是解决方案,但我真的不知道我该怎么做。该程序必须非常灵敏,因为它是一个 UDP 流记录器,如果它阻塞太久,这将在文件中产生滞后。

非常感谢。

PS.:仅供参考,我仍在学习,所以即使解决方案可能很明显,请不要责怪我。


编辑:

这是一些伪代码:

当录制请求进来时,我创建一个新连接并连接到流地址。如果成功,我使用以下函数构建我的 fdset:

build_fdset()
{
    int ii;
    /* */
    FD_ZERO(&fdset);
    /* */
    for (ii = 0; ii < max; ii++)
    {
        if (astRecorder[ii].bUsed != FALSE && astRecorder[ii].socket != INVALID_SOCKET)
        {
            FD_SET(astRecorder[ii].socket,&fdset);
            /* */
            if (astRecorder[ii].socket > maxSocket)
                maxSocket = astRecorder[ii].socket;
        }
    }
}

然后是处理连接的循环:

main_loop()
{
    struct timeval timeout;
    /* */
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    /* */
    for (;;)
    {   
        memcpy(&fdset_cpy,&fdset,sizeof(fdset));

        int ret = select((maxSocket + 1) , &fdset_cpy, NULL, NULL, &timeout);

        if (iSelectRet <= 0)
            continue;
        else
        {
            int ii;

            for(ii = 0; ii < max; ii++)
            {
                if ((recorders[ii].bUsed) && (FD_ISSET(recorders[ii].socket, &fdset_cpy)))
                {
                    /* receive from socket */
                    /* handle received data */
                }
            }
        }
    }
}

问题:当我将超时设置为 timeout.tv_sec = 1 timeout.tv_usec = 0 时,一切正常,但我得到 100% 的 CPU 使用率!当我将 NULL 作为超时时,尽管套接字上有数据,但程序会在 select() 上阻塞。


解决方案:

好吧,我终于找到了错误!在上面的代码中,我只在主循环之前设置了一次超时值。那么问题在于,对于 fdset,超时结构是由 select() 函数修改的。所以在第一次正确的超时选择之后,超时结构被select()函数修改并设置为0。这导致0超时,因此问题是下一次循环到达选择函数时,超时给定到选择是0!!!

仍然非常感谢那些试图提供帮助的人!我很欣赏它=)

【问题讨论】:

  • 除了您在build_fdset 中使用fd_setFlag 和在main_loop 中使用fdset 之外,我看不出您的代码有任何直接错误。
  • 有趣的是在原始代码中两个函数都有fdset。我稍微修改了代码,把它贴在这里,忘记在上面的函数中重命名 fd_setFlag。所以这绝对不是错误!
  • 如果我将超时设置为 NULL,我只是不明白为什么它会阻塞
  • 我注意到的另一件事是,当我将超时设置为 1 秒时,它似乎不会等待一秒,但 select 会立即返回
  • 只有在套接字上没有传入流量时它才应该阻塞。当集合中的一个套接字可读时,select 应返回可读取的套接字数。

标签: c linux networking


【解决方案1】:

您还可以在检查所有流以在循环结束时放弃 CPU 后使用 sleep。这样一来,您就不必依赖单个流在不久的将来某个时间接收传入数据,从而冒着无法为其他流提供服务的风险。

【讨论】:

    【解决方案2】:

    select 调用的超时时间可以是NULL,这意味着永远等待。

    【讨论】:

    • 那么你的实际意思是,如果我设置一个 NULL 超时,select 将永远阻塞,直到数据可用?
    • 好吧,我一直在尝试这个,我得到的结果是,当我通过 NULL 超时时,即使套接字上有数据,选择也永远不会继续。如果我将超时设置为 1 秒,一切正常(我可以从套接字读取),但最初的问题是 CPU 跳到 100%
    • @gekod 我认为是时候展示一些代码了,更准确地说是包含 select 的循环。
    猜你喜欢
    • 2013-12-05
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 2020-03-28
    • 1970-01-01
    相关资源
    最近更新 更多