2011年6月14日更新
快速更新......大多数受访者都专注于处理要记录的消息队列的狡猾方法,但是当然缺乏优化时,它肯定不是问题的根源。我们将Yield转换为短暂的睡眠(是的,一旦系统安静,Yield确实会产生100%的CPU)但是系统仍然无法跟上日志记录,即使它没有接近睡眠状态。从我所看到的发送只是不是非常有效。一位受访者评论说我们应该将Send()一起阻塞到一个发送中,这似乎是对更大的潜在问题的最合适的解决方案,这就是为什么我将此标记为原始问题的答案。我当然同意队列模型存在很大的缺陷,所以感谢对此的反馈,我已经投了所有为讨论做出贡献的答案。
然而,这个练习让我们回顾为什么我们正在使用像我们这样的套接字上的外部日志记录,虽然之前当日志记录服务器做了很多事情时它可能已经有意义了处理日志条目...它不再执行任何操作,因此我们选择远程访问整个模块并通过一些预先存在的日志记录框架寻求直接到文件的方法,这应该完全消除问题以及消除系统中不必要的复杂性。
再次感谢所有反馈。
原始问题
在我们的系统中,我们有两个对这个问题很重要的组件 - 一个是用Visual C ++开发的,另一个是Java(不要问,历史原因)。
C ++组件是主要服务并生成日志条目。这些日志条目通过CSocket :: Send out发送到Java日志服务。
问题
发送数据的性能似乎非常低。如果我们在C ++端排队,那么队列将在繁忙的系统上逐步备份。
如果我使用一个简单的C#应用程序访问Java Logging Server,那么我可以更快地锤击它,然后我将需要使用C ++工具并保持良好状态。
在C ++世界中,向队列添加消息的功能是:
void MyLogger::Log(const CString& buffer)
{
struct _timeb timebuffer;
_ftime64_s( &timebuffer );
CString message;
message.Format("%d%03d,%04d,%s\r\n", (int)timebuffer.time, (int)timebuffer.millitm, GetCurrentThreadId(), (LPCTSTR)buffer);
CString* queuedMessage = new CString(message);
sendMessageQueue.push(queuedMessage);
}
在发送到套接字的单独线程中运行的函数是:
void MyLogger::ProcessQueue()
{
CString* queuedMessage = NULL;
while(!sendMessageQueue.try_pop(queuedMessage))
{
if (!running)
{
break;
}
Concurrency::Context::Yield();
}
if (queuedMessage == NULL)
{
return;
}
else
{
socket.Send((LPCTSTR)*queuedMessage, queuedMessage->GetLength());
delete queuedMessage;
}
}
请注意,ProcessQueue由外部循环线程本身重复运行,不包括一堆无意义的前导码:
while(parent->running)
{
try
{
logger->ProcessQueue();
}
catch(...)
{
}
}
队列是:
Concurrency::concurrent_queue<CString*> sendMessageQueue;
所以我们看到的效果是队列变得越来越大,日志条目被发送到套接字但速度远低于它们的进展速度。
这是CSocket :: Send的限制吗?这对我们来说不是很有用吗?误用了吗?或者整个红鲱鱼,问题出在其他地方?
非常感谢您的建议。
亲切的问候
Matt Peddlesden
答案 0 :(得分:3)
好吧,你可以先使用阻塞生产者 - 消费者队列,然后摆脱'收益'。消息被阻塞我并不感到惊讶 - 当一个消息发布时,记录器线程通常是在繁忙的系统上准备好但没有运行。这将在处理队列上的任何消息之前引入大量可避免的延迟。后台线程比有一个量子试图摆脱队列中累积的所有消息。如果繁忙的系统上有很多就绪线程,那么线程可能没有足够的时间来处理消息。特别是如果已经建立了很多并且socket.send阻塞。
此外,在队列轮询中几乎完全浪费一个CPU内核可能不利于整体性能。
RGDS, 马丁
答案 1 :(得分:1)
这里的事情可能会减慢你的速度:
答案 2 :(得分:1)
在我看来,你绝对不是在寻找最有效的解决方案。你一定应该调用Send()一次。对于所有消息。连接用户端队列中的所有消息,使用Send(),然后 yield一次性发送所有消息。
此外,这真的不是你的意思。 PPL包含明确用于异步回调的构造 - 如call
对象。你应该使用它而不是自己动手。
答案 3 :(得分:0)
您是否尝试过分析以查看应用程序遇到问题的位置?是仅在记录发件人存在问题时?是CPU绑定还是阻塞?
我唯一能看到的是你没有用任何类型的锁来保护消息队列,所以容器状态可能会变得奇怪,导致各种意外行为。