我正在编写一个 Windows 7 可视化 C++ 服务器应用程序,它应该接收 3.6 MB/s 的 UDP 数据报。我有一个主线程,recvfrom() 接收数据。该套接字是一个非阻塞套接字,具有 64kB 的接收缓冲区。如果套接字上没有接收到数据,则线程执行 sleep(1)。
我的问题是线程占用了我双核处理器的近 50%,我不知道如何减少它。Wireshark 只使用了 20%,所以我的主要目标是达到类似的百分比。
你有什么想法?
我正在编写一个 Windows 7 可视化 C++ 服务器应用程序,它应该接收 3.6 MB/s 的 UDP 数据报。我有一个主线程,recvfrom() 接收数据。该套接字是一个非阻塞套接字,具有 64kB 的接收缓冲区。如果套接字上没有接收到数据,则线程执行 sleep(1)。
我的问题是线程占用了我双核处理器的近 50%,我不知道如何减少它。Wireshark 只使用了 20%,所以我的主要目标是达到类似的百分比。
你有什么想法?
您可以使用类似选择的方法来等待数据到达您的套接字或客户端决定关闭,而不是轮询:
首先使您的套接字非阻塞:
u_long nonBlocking = 0;
WSAEventSelect(sock, NULL, 0);
ioctlsocket(sock, FIONBIO, &nonBlocking);
然后使用WSAWaitForMultipleEvents等待数据到达或您想取消接收:
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;
}
如果要取消recv,客户端代码会调用WSASetEvent
。recvCancelEvt
虽然基于 Select 或阻塞套接字的解决方案是正确的方法,但您以 100% 运行一个内核的原因是由于 Sleep 的行为:-
查看WinAPI sleep()的文档:
此函数使线程放弃其时间片的剩余部分,并在基于 dwMilliseconds 值的时间间隔内变得不可运行。系统时钟以恒定速率“滴答”。如果 dwMilliseconds 小于系统时钟的分辨率,则线程可能休眠的时间少于指定的时间长度。
因此,如果您正在轮询,您要么需要使用更大的睡眠时间(可能是 20 毫秒,这通常比 Windows 滴答率大一点),要么使用更准确的多媒体计时器。
看起来大多数时候您对 recvfrom 的调用不会返回数据。睡眠 1 毫秒并不多。您应该考虑增加睡眠时间(便宜,但不是最佳解决方案),或者更好的解决方案,考虑使用事件驱动的方法。使用select()
或 Windows API 阻塞,直到套接字发出信号或发生您感兴趣的其他事件,然后调用recvfrom
. 您可能需要为此重新设计程序的主循环。
I would recommend using a boost::asio::io_service. We receive about 200MB/s of UDP multicast traffic while maxing out a modern CPU. This includes a full reliability protocol and data dispatch to the application. The bottleneck in profiling is the processing, not the boost::asio receive. Code here