Indy10文件传输导致CPU使用率100%

发布于 2024-10-02 13:00:00 字数 1831 浏览 5 评论 0原文

我设法修复了一些断开连接的错误,现在每当文件传输时 CPU 使用率变为 100%,我不知道我做错了什么:S.....

const
 MaxBufferSize = 1024;

type
 TClient = class(TObject)
 public
  AContext: TIdContext;
  FileSize: Integer;
  Canceled: Boolean;
  Transfered: Integer;
  procedure ReceiveData;
  procedure Update;
 end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
 Data: string;
 Client: TClient;
 Item: TListItem;
begin
 Data := AContext.Connection.IOHandler.ReadLn;

 //Data := 'SEND|785548' = Command + | + FileSize
 if Copy(Data, 1, 4) = 'SEND' then
 begin
  Delete(Data, 1, 5);
  Client := TClient.Create;
  Client.FileSize := StrToInt(Data);
  Client.AContext := AContext;
  Item := ListView1.Items.Add;
  Item.Caption := AContext.Connection.Socket.Binding.PeerIP;
  Item.Data := Client;
  Client.ReceiveData;
 end;
end;

procedure TClient.ReceiveData;
var
 currRead : Integer;
 FS: TFileStream;
begin
 Canceled := False;
 FS := TFileStream.Create('C:\Test.dat', fmCreate or fmOpenReadWrite);
 FS.Size := 0;
 Transfered := 0;
 try
  while (FS.Position < FileSize) and (Athread.Connection.Connected) and (not Canceled) do
  begin
   Application.ProcessMessages;
   if (FileSize - FS.Position) >= MaxBufferSize then currRead := MaxBufferSize
   else currRead := (FileSize - FS.Position);
   AThread.Connection.IOHandler.ReadStream(FS, CurrRead);
   Transfered := FS.Position;
   Notify.NotifyMethod(Update);
   Application.ProcessMessages;
  end;
 finally
  FS.Free;
  AThread.Connection.IOHandler.InputBuffer.Clear;
  AThread.Connection.Disconnect;
  AThread.RemoveFromList;
  Notify.NotifyMethod(Update);
  Application.ProcessMessages;
 end;
end;

procedure TClient.Update;
begin
 //Code to Display Progress bar and stuff (Simplified for now)
 Form1.Label1.Caption := 'Transfered Data : ' + IntToStr(Transfered);
end;

i managed to fix some errors with disconnecting, now whenever a file is transferring the CPU Usage becomes 100%, i dunno what im doing wrong :S.....

const
 MaxBufferSize = 1024;

type
 TClient = class(TObject)
 public
  AContext: TIdContext;
  FileSize: Integer;
  Canceled: Boolean;
  Transfered: Integer;
  procedure ReceiveData;
  procedure Update;
 end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
 Data: string;
 Client: TClient;
 Item: TListItem;
begin
 Data := AContext.Connection.IOHandler.ReadLn;

 //Data := 'SEND|785548' = Command + | + FileSize
 if Copy(Data, 1, 4) = 'SEND' then
 begin
  Delete(Data, 1, 5);
  Client := TClient.Create;
  Client.FileSize := StrToInt(Data);
  Client.AContext := AContext;
  Item := ListView1.Items.Add;
  Item.Caption := AContext.Connection.Socket.Binding.PeerIP;
  Item.Data := Client;
  Client.ReceiveData;
 end;
end;

procedure TClient.ReceiveData;
var
 currRead : Integer;
 FS: TFileStream;
begin
 Canceled := False;
 FS := TFileStream.Create('C:\Test.dat', fmCreate or fmOpenReadWrite);
 FS.Size := 0;
 Transfered := 0;
 try
  while (FS.Position < FileSize) and (Athread.Connection.Connected) and (not Canceled) do
  begin
   Application.ProcessMessages;
   if (FileSize - FS.Position) >= MaxBufferSize then currRead := MaxBufferSize
   else currRead := (FileSize - FS.Position);
   AThread.Connection.IOHandler.ReadStream(FS, CurrRead);
   Transfered := FS.Position;
   Notify.NotifyMethod(Update);
   Application.ProcessMessages;
  end;
 finally
  FS.Free;
  AThread.Connection.IOHandler.InputBuffer.Clear;
  AThread.Connection.Disconnect;
  AThread.RemoveFromList;
  Notify.NotifyMethod(Update);
  Application.ProcessMessages;
 end;
end;

procedure TClient.Update;
begin
 //Code to Display Progress bar and stuff (Simplified for now)
 Form1.Label1.Caption := 'Transfered Data : ' + IntToStr(Transfered);
end;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

紙鸢 2024-10-09 13:00:00

摆脱Application.ProcessMessages;它不能在主线程以外的线程下调用

Get rid of Application.ProcessMessages; it MUST NOT be called under thread other than main one

信愁 2024-10-09 13:00:00

您在接收循环中调用 Application.ProcessMessages ,大概是为了防止应用程序的其余部分看起来被冻结。 100% CPU 使用率是一个副作用。

您最好使用 IdAntiFreeze 组件(仍然是一种 hack)或将 ReceiveData 功能放在线程中。

更新:

哎呀。乍一看,我以为这是在主线程中运行的客户端传输,但实际上它是在单独的 IdTcpServer 线程中调用的。在这种情况下,APZ28的建议是正确的;不要在线程中调用Application.ProcessMessages。

You're calling Application.ProcessMessages in your receive loop, presumably to keep the rest of your application from appearing to be frozen. The 100% CPU usage is a side effect.

You're better off using an IdAntiFreeze component (still kind of a hack) or putting the ReceiveData functionality in a thread.

Update:

Whoops. At first glance, I thought this was a client side transfer running in the main thread, but it's actually being called in a separate IdTcpServer thread. In this case, APZ28's advice is correct; don't call Application.ProcessMessages in a thread.

听风念你 2024-10-09 13:00:00

我对 Indy 一无所知(我使用自己的设备,它比 Indy 更轻/更快,用于所有 TCP/IP 客户端/服务器内容 - 请参阅 http://synopse.info),但我猜你的 IdTCPServer1Execute 方法应该在后台线程中运行,但这里的情况并非如此。

所以:

  1. 摆脱所有那些Application.ProcessMessages之类的;
  2. 使用计时器来同步您的 UI(一秒刷新读取传输的字节数就足够了),而不是使用 Notify() 或 Synchronize() 方法;
  3. 确保您的 IdTCPServer1 组件在单独的线程中运行(应该有一些属性可以做到这一点);
  4. 另一种可能性(可能性很小)是 ReadStream 方法不必这样调用,并且不以 CPU 友好的方式等待数据;如果是这种情况,Indy 应该提供一些方法来等待待处理的数据,而不会阻塞。
  5. 使用探查器(有一些免费的探查器 - 例如 http://delphitools.info )来猜测 CPU 烧毁的位置。
  6. 在 IDE 之外运行 - 是相同的行为吗?

I dont' know anything about Indy (I use my own unit, which is lighter/faster than Indy, for all TCP/IP client/server stuff - see http://synopse.info), but I guess your IdTCPServer1Execute method should run in a background thread, which is not the case here.

So :

  1. Get rid of all those Application.ProcessMessages and such;
  2. Use a timer to synchronize your UI (a one second refresh reading the transferred byte count is enough), and not a Notify() nor Synchronize() method;
  3. Ensure that your IdTCPServer1 component is running in a separate thread (there should be some property to do that);
  4. Another possibility (very unlikely) is that the ReadStream method don't have to be called as such, and don't wait for data in a CPU-friendly way; if it's the case, there should be some method made available by Indy to wait for pending data, without blocking.
  5. Use a profiler (there are some free one around - like http://delphitools.info ) to guess where your CPU is burnt.
  6. Run outside the IDE - is it the same behavior?
染墨丶若流云 2024-10-09 13:00:00

如果您不断循环,一个简单的技巧就是在 Application.ProcessMessages 之后添加 Sleep(1)

但也许您可以重新排序代码以阻止 ReadStream 函数,并且仅在接收到合理数量的数据或超时后才运行。

Your looping constantly, an easy hack is to add an Sleep(1) after your Application.ProcessMessages.

But perhaps you could reorder your code to block on the ReadStream function, and only run when a reasonable amount of data is received, or a timeout has passed.

简美 2024-10-09 13:00:00

You and I covered this topic in the Embarcadero forums (cached on CodeNewsFast).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文