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

3

我成功解决了一些有关断开连接的错误,但现在每当文件传输时,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;
5个回答

6

摆脱Application.ProcessMessages; 在主线程之外的线程中不得调用


1
此外,要摆脱 ListView1.Items.Add 调用;它是 VCL 方法,因此不应从 VCL 线程外部调用。 - Rob Kennedy
同样的效果,现在CPU使用率从98%到100%不等。 - killercode

4
您正在调用Application.ProcessMessages来保持接收循环的其余部分不会显示为冻结状态,这可能导致100%的CPU使用率。您最好使用IdAntiFreeze组件(还是一种hack方法),或将ReceiveData功能放入一个线程中。
更新:
糟糕,乍一看,我以为这是在主线程中运行的客户端传输,但实际上它是在单独的IdTcpServer线程中调用。在这种情况下,APZ28的建议是正确的; 不要在线程中调用Application.ProcessMessages。

2
我不知道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 外部运行 - 行为是否相同?

我已经尝试了几乎所有的方法,除了非阻塞流读取,我真的不知道该怎么做......你有任何想法吗?谢谢回答。 - killercode
好的,谢谢。你们的组件支持Unicode吗?因为这就是我选择首先使用Indy10的原因。 - killercode
当然,SynCrtUnit在Delphi 6到Delphi XE中都可以使用并进行测试。它使用AnsiString类型,您可以轻松地将其转换为Unicode字符串。 - Arnaud Bouchez

1

你的循环一直在运行,一个简单的技巧是在Application.ProcessMessages之后添加Sleep(1)

但也许你可以重新排列代码,在ReadStream函数上阻塞,只有在接收到合理数量的数据或超时后才运行。


它将 CPU 使用率降低到了 90%,但现在传输速度变慢了,我该如何做这个阻塞的事情?实际上,客户端正在以1024字节块发送数据。 - killercode
е°қиҜ•еҲ йҷӨжүҖжңүзҡ„Application.ProcessMessagesе’ҢNotify.NotifyMethod(Update);гҖӮ - Davy Landman
所有的Application.ProcessMessages都被移除了,在移除Notify之后没有任何区别。 - killercode
那个阻塞的事情可能在Indy中不可能实现,如果我没记错的话,Indy是基于事件的。因此,您可以使用事件处理程序来处理新数据,如果您想要一个简单的循环,请使用Synopse,以更酷的方式执行TCP协议(阻塞)。 - Davy Landman

1

你能看一下这个吗?http://plunder.com/80961bd7e3 抱歉如果有错误,我一直在尝试让它工作 :( - killercode
在论坛上:指定的主题[46075]未找到。有人缓存了吗?(它不在web.archive.org上) - Jeroen Wiert Pluimers
Embarcadero的新闻组/论坛已经被缓存到codenewsfast - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接