UDP recvfrom线程使用过多的CPU资源

时间:2013-01-18 09:58:30

标签: c++ multithreading sockets udp winsockets

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

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

你有什么想法吗?

4 个答案:

答案 0 :(得分:4)

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

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

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;
    }
    if (0 != WSAEventSelect(handle->iSocket, evt, FD_READ|FD_CLOSE)) {
        WSACloseEvent(event);
        return;
    }

    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;
}

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

答案 1 :(得分:1)

虽然基于Select或阻塞套接字的解决方案是正确的方法,但是你以100%运行一个核心的原因是由于Sleep的行为: -

查看WinAPI sleep()的文档:

  

此函数使线程放弃剩余的时间   基于值的间隔切片并变得不可用   dwMilliseconds。系统时钟以恒定速率“滴答”。的 如果   dwMilliseconds小于系统时钟的分辨率   线程可能会睡眠时间少于指定的时间长度。

因此,如果您正在进行轮询,则需要使用更长的睡眠时间(可能是20M,通常比Windows滴答速率稍高),或者使用更准确的多媒体计时器。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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