【问题标题】:UDP recvfrom thread use too much CPU resourcesUDP recvfrom 线程占用过多 CPU 资源
【发布时间】:2013-01-02 00:20:53
【问题描述】:

我正在编写一个 Windows 7 Visual C++ 服务器应用程序,它应该接收 3.6 MB/s 的 UDP 数据报。 我有一个主线程,recvfrom() 接收数据。该套接字是一个非阻塞套接字,具有 64kB 的接收缓冲区。如果套接字上没有接收到数据,则线程执行 sleep(1)。

我的问题是线程占用了我双核处理器的近 50%,我不知道如何减少它。 Wireshark 只使用了其中的 20%,所以我的主要目标是达到类似的百分比。

你有什么想法吗?

【问题讨论】:

  • 如果你觉得勇敢,你可以考虑使用 Boost::async - 但它本质上是@simonc 回答关于使用包装在类库中的选择。

标签: c++ multithreading sockets udp winsockets


【解决方案1】:

您可以使用类似选择的方法来等待数据到达您的套接字或客户端决定关闭,而不是轮询:

首先让你的套接字非阻塞:

u_long nonBlocking = 0;
WSAEventSelect(sock, NULL, 0);
ioctlsocket(sock, FIONBIO, &nonBlocking);

然后使用WSAWaitForMultipleEvents 等待数据到达或您想取消recv:

int32_t MyRecv(THandle sock, WSAEVENT* recvCancelEvt,
               uint8_t* buffer, uint32_t bufferBytes)
{
    int32_t bytesReceived;
    WSAEVENT evt;
    DWORD ret;
    HANDLE handles[2];

    event = WSACreateEvent();
    if (NULL == evt) {
        return -1;
    }
    if (0 != WSAEventSelect(handle->iSocket, evt, FD_READ|FD_CLOSE)) {
        WSACloseEvent(evt);
        return -1;
    }

    bytesReceived = recv(sock, (char*)buffer, bufferBytes, 0);
    if (SOCKET_ERROR==received && WSAEWOULDBLOCK==WSAGetLastError()) {
        handles[0] = evt;
        handles[1] = *recvCancelEvt;
        ret = WSAWaitForMultipleEvents(2, handles, FALSE, INFINITE, FALSE);
        if (WAIT_OBJECT_0 == ret) {
            bytesReceived = recv(handle->iSocket, (char*)buffer, bufferBytes, 0);
        }
    }
    WSACloseEvent(evt);
    return bytesReceived;
}

如果要取消接收,客户端代码会在 recvCancelEvt 上调用 WSASetEvent

【讨论】:

  • 感谢您的回答。你的例子很有启发性。无论如何,我必须意识到不是我简单的接收机制效率低下,而是我处理数据的方式。
【解决方案2】:

在大多数情况下,您对 recvfrom 的调用似乎都没有返回数据。睡眠 1 毫秒并不多。您应该考虑增加睡眠时间(便宜,但不是最佳解决方案),或者更好的解决方案,考虑使用事件驱动的方法。使用select() 或Windows API 阻塞,直到套接字发出信号或发生您感兴趣的其他事件,然后调用recvfrom。您可能需要为此重新设计程序的主循环。

【讨论】:

    【解决方案3】:

    虽然基于选择或阻塞套接字的解决方案是正确的方法,但您以 100% 运行一个内核的原因是由于睡眠的行为:-

    查看WinAPI sleep() 的文档:

    此函数使线程放弃其剩余时间 根据 dw 毫秒。系统时钟以恒定速率“滴答”。 如果 dwMilliseconds 小于系统时钟的分辨率,则 线程可能休眠的时间少于指定的时间长度。

    因此,如果您正在轮询,您要么需要使用更长的睡眠时间(可能是 20 毫秒,这通常比 Windows 的滴答率要大一点),要么使用更准确的多媒体计时器。

    【讨论】:

      【解决方案4】:

      我建议使用 boost::asio::io_service。我们收到大约 200MB/s 的 UDP 多播流量,同时最大限度地利用现代 CPU。这包括完整的可靠性协议和应用程序的数据分派。分析的瓶颈是处理,而不是 boost::asio 接收。 Code here

      【讨论】:

      • 我还没有厌倦boost库,但主要问题是处理而不是接收。
      猜你喜欢
      • 1970-01-01
      • 2012-08-26
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      • 2011-12-15
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多