Delphi 中 CPU 占用率达到 100%

6
我可以帮您翻译成中文。这段内容是关于使用Delphi 5中的Indy 9组件来通过UDP与网络设备进行通信的。使用UDPServer组件,将其放入一个从TThread派生的类中。如果编写类似以下代码,则CPU使用率为100%。
在线等,谢谢!
while not terminated do begin
  if GetMessage(Msg, 0, 0, 0) then begin
    if Msg.message = WM_UDPMSG then
      Break
    else
      DispatchMessage(Msg);
  end;
end;

在UDP读取事件中:
  try    
    // Processing the data here
  except
    PostThreadMessage(ThreadId, WM_UDPMSG, 0, 0);
  end;

当我在while-do循环或OnUDPRead事件中使用Sleep函数时,没有任何变化。仍然会导致CPU使用率达到100%。
我的线程优先级为Normal。
我该如何解决这个问题?
6个回答

6
你遇到的问题是因为你在GUI线程中接收UDP数据,但想在另一个线程中处理数据。
真正的问题是你试图以阻塞方式使用异步组件。更好的解决方案是使用真正的阻塞UDP通信库,比如synapse。然后只需在你的线程中等待新数据即可。
你可以这样写:
while not Terminated do
begin
  BytesRead := FSocker.RecvBufferEx(@(Buffer[0]), BufferSize, Timeout);
  if (BytesRead = 0) then
  begin
    // continue or exit if the receiving Failed
    case FSocket.LastError of
      0, WSAETIMEDOUT: Continue;
      WSAECONNRESET, WSAENETRESET,
        WSAENOTCONN, WSAECONNABORTED,
        WSAENETDOWN:
        begin
          CloseConnection;
          Exit;
        end;
    else
      CloseConnection;
      Exit;
    end;    
  end;
  // process the data in the buffer
end;

GetMessage已经是阻塞的了,所以不需要信号量。 - The_Fox
@The_Fox:你说得对,我完全忘记了!(已经有一段时间没有直接编程到Windows消息系统了)。我从我的答案中提取了那部分。 - Davy Landman
WSAETIMEDOUT 表示您在指定的超时时间内没有收到 UDP 消息。因此,如果您设置了 100ms 的超时时间,您将会收到很多 WSAETIMEDOUT 错误,如果您将其设置为 10000ms,则会收到更少的超时错误。 - Davy Landman
我已经查看了TUDPBlockSocket的文档(http://synapse.ararat.cz/doc/help/blcksock.TUDPBlockSocket.html),它提到RecvPacket更受欢迎,你可以尝试使用它。 - Davy Landman
你是否读过Indy和Synapse在支持UDP监听器绑定所有接口方面的差异(http://www.ararat.cz/synapse/doku.php/public:howto:multiplebind)?显然,Indy是虚拟的。例如:如果你测试本地流量,应该使用127.0.0.1。 - Davy Landman
显示剩余3条评论

3

我不熟悉Delphi代码,但你正在运行一个busy-wait机制,这会使你的CPU占用率极高。

向循环中引入sleep或delay只是掩盖了问题。我建议使用更好的方法来接收消息/事件。有许多解决方案,如观察者-监听器模式或线程等待和通知方案。


以下是对您评论的一些有用链接:


谢谢你的回答。有很多解决方案,你有任何示例或链接吗? - SimaWB
我不相信他在发布的代码中使用了忙等待机制。GetMessage会一直阻塞,直到有消息到来,因此不会出现忙等待。他还说Sleep也没有帮助。即使是sleep(1),CPU使用率也只有0-1%。他的问题在其他地方。 - The_Fox

2

1 您需要使用新于9.0.0.18版本的Indy。旧版存在严重的线程错误,会导致程序无法运行。这包括Delphi 7版本及以前的所有版本附带的Indy。

2 查看示例代码,了解如何使用Indy。

http://www.indyproject.org/demos/index.html


最后为服务器组件点个赞。如果它们在多核或超线程系统上运行,一些服务器组件在早期版本(包括单核P4及其HT)上会表现得很奇怪。 - Marco van de Voort

1

是否有一种等待(阻塞线程)直到消息到达的GetMessage版本?


1

我不知道GetMessage的版本。但是在Windows.pas中声明如下:

function GetMessage; external user32 name 'GetMessageA';

0

这个项目非常庞大。因此,对我来说更新Indy很困难。但是如果您确定问题是由于旧版本的Indy引起的,我会进行更新。

我已经查看了所有Indy演示文稿。这些演示非常简单。在我的项目中,我需要非常快速的数据传输。(例如实时音频录制)


如果你不想让你的应用程序崩溃,更新Indy是必须的。我已经能够在一个小时内可靠地使Indy应用程序崩溃。 - Stephan Eggermont

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