views:

1022

answers:

6

I use Indy 9 with Delphi 5. In my application I want to communicate with a network device via UDP. So I use UDPServer comp. in a class which is derived from TThread. When I write similar to the following code then CPU usage is 100%.

in the thread :

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;

and OnUDPRead event :

  try    
    // Processing the data here
  except
    PostThreadMessage(ThreadId, WM_UDPMSG, 0, 0);
  end;

When I use Sleep function in the while-do loop or in OnUDPRead event there is no change. Still the CPU usage is 100%.

My thread priority is Normal.

How can I solve my problem?

+3  A: 

I'm not intimate with delphi code, but you are running a busy-wait mechanism which is grinding your CPU.

Introducing a sleep or a delay to the loop only hides the problem. I suggest using a better method for receiving your messages/events. Many solutions exist, like the observer-listener pattern, or thread wait and notify schemes.


Some helpful links in response to your comment:

Yuval A
Thanks for the answer. "Many solutions exist" Dou you have any example or link ?
SimaWB
no problem. added links to answer
Yuval A
I'm not convinced he is using a busy-wait mechanism in the code he posted. GetMessage blocks till there is a message, so no busy wait. He also says that a Sleep doesn't help. Even a sleep(1) will get your cpu usage to 0-1%. His problem is somewhere else.
The_Fox
+1  A: 

Is there a version of GetMessage which waits (blocks the thread) until a message arrives?

James L
GetMessage does block the thread, PeekMessage doesn't.
The_Fox
+1  A: 

I don't know the verison of GetMassage. But it declared in Windows.pas like this

function GetMessage; external user32 name 'GetMessageA';
SimaWB
GetMassage? Ah, if only...
Lieven
+1  A: 

1 You need a version of Indy newer than 9.0.0.18, I think. Older ones have show-stopping threading bugs. That includes all versions of Indy delivered with Delphi up to version 7.

2 Take a look at the sample code on how to work with Indy.

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

Stephan Eggermont
A: 

This project is very big project. So updating Indy is difficult for me. But if you sure the problem is because of old version of Indy, I'll update it.

I've looked at all Indy demos. These demos are very simple. In my project I've very fast data transfer. (Like real time sound recorder)

SimaWB
Updating Indy is a must if you want your app not to crash. I've been able to reliably crash Indy apps within an hour.
Stephan Eggermont
+4  A: 

The problem you have is because you're receiving the UDP data in the GUI thread, but want to process the data in another thread.

The real problem is that your trying to use a asynchronous component in a blocking way. The better solution would be to use a real blocking UDP communication library such as synapse. Then it's very easy to just wait for new data to receive in your thread.

You could just write:

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;
Davy Landman
GetMessage is already blocking, so a semaphore is not needed.
The_Fox
@The_Fox: your right, I totally forgot about that! (Been a while since I've programmed that directly to the windows message system). I've extracted that part out of my answer.
Davy Landman
Thanks The_Fox and Davy Landman
SimaWB
Why I always get WSAETIMEDOUT message?
SimaWB
WSAETIMEDOUT means you haven't recieved an UDP message for the specified timeout. So if you have a timeout for 100ms, you'll get allot of WSAETIMEDOUT, if you set it to 10000ms you'll get a lot less timeouts.
Davy Landman
I've check the documentation for TUDPBlockSocket (http://synapse.ararat.cz/doc/help/blcksock.TUDPBlockSocket.html) it mentions RecvPacket is preferred, you could try this.
Davy Landman
But I'm sure I receive data in 1000 ms. (In fact every 5 ms) I see with Wireshark. And also when I use Winsock I receive datas.
SimaWB
I've tried this demo : http://www.ararat.cz/synapse/doku.php/public:howto:udpserverIt's very strange but I got same error.
SimaWB
Have you read that there is a difference between the way Indy supports a udp listener bind over all interfaces and synapse does not (http://www.ararat.cz/synapse/doku.php/public:howto:multiplebind). Apparently Indy does this virtual. Ex: if your testing local trafic you should use 127.0.0.1.
Davy Landman