首页 > 解决方案 > Delphi RIO - Indy TCPServer 高 CPU 使用率

问题描述

我有一个用 Delphi RIO + Indy TCP Server 开发的简单 TCP 文件服务器程序。当 2 个或更多客户端请求文件时,CPU 在 90 年代运行非常高。这吓坏了服务器团队,在此期间,他们很难登录到运行程序的服务器。

基于该主题的其他线程,当我放置 IndySleep(x) 时,它确实降低了 CPU 并且平均保持在 50-60 秒。我知道放置 IndySleep() 可能会有点节流,但它有效!

它提供的文件已经压缩,大小从 1KB 到 <10MB 不等。

在没有或几乎没有 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;

标签: delphiindy

解决方案


你打电话到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;

推荐阅读