问题描述
我有一个用Delphi RIO + Indy TCP Server开发的简单TCP文件服务器程序。当2个或更多客户端请求文件时,cpu会在90s内运行很高。这吓坏了服务器团队,在此期间,他们很难登录到运行该程序的服务器。
基于主题的其他线程,当我放置IndySleep(x)时,它确实使cpu瘫痪,并且平均停留时间为50-60s。我了解将IndySleep()放入可能会有所限制,但它确实有效!
它提供的文件已被压缩,大小从1KB到
在没有或几乎没有IndySleep()的情况下,我还能采取其他措施来提高总体cpu使用率吗?
这是代码段:
procedure TMainForm.IdTcpsyncServerExecute(AContext: TIdContext);
begin
if (not AContext.Connection.IOHandler.InputBufferIsEmpty)
and (AContext.Connection.Connected) then
begin
SendFile(AContext,AContext.Connection.IOHandler.ReadLn);
//IndySleep(1000 div IdTcpsyncServer.Contexts.Count); // For high cpu
IndySleep(500); // For high cpu
end;
end;
procedure TMainForm.SendFile(AContext: TIdContext; AFileName: string);
var
lStream: TFileStream;
begin
lStream := TFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite);
if Assigned(lStream) then
begin
try
WriteRespHeader(AContext,1,lStream.Size); //Custom fn() writes back to client file size and other useful info
AContext.Connection.IOHandler.LargeStream := False; // 32-bit
lStream.Position := 0;
AContext.Connection.IOHandler.Write(lStream,lStream.Size);
finally
lStream.Free;
end;
AddLogMsg(AContext.Binding.PeerIP + ' : Sent File: ' + AFileName); // Thread.Queue() based logging
end;
end;
解决方法
您在错误的地方打了IndySleep()
的电话。如果客户端没有可供读取的内容,则您将立即退出OnExecute
并立即返回,从而形成紧密循环。那就是您的CPU使用率很高的地方。仅在尚无可用空间时进入睡眠状态,例如:
procedure TMainForm.IdTCPSyncServerExecute(AContext: TIdContext);
begin
if (not AContext.Connection.IOHandler.InputBufferIsEmpty)
and (AContext.Connection.Connected) then
begin
SendFile(AContext,AContext.Connection.IOHandler.ReadLn);
end else begin
//IndySleep(1000 div IdTCPSyncServer.Contexts.Count); // For high CPU
IndySleep(500); // For high CPU
// or,use AContext.Connection.IOHandler.Readable() instead...
// or,use AContext.Connection.IOHandler.CheckForDataOnSoure() instead...
end;
end;
或者,我通常建议使用这种手动检查:
procedure TMainForm.IdTCPSyncServerExecute(AContext: TIdContext);
begin
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(500{1000 div IdTCPSyncServer.Contexts.Count}); // For high CPU
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then Exit;
end;
SendFile(AContext,AContext.Connection.IOHandler.ReadLn);
end;
但是,实际上,在这种情况下,更好的解决方案是根本不手动检查客户端数据的存在。如果没有什么可以读取的,只需IOHandler.ReadLn()
阻止,直到有实际的东西到达为止,例如:
procedure TMainForm.IdTCPSyncServerExecute(AContext: TIdContext);
begin
SendFile(AContext,AContext.Connection.IOHandler.ReadLn);
end;