Newsgroups : Borland : borland.public.delphi.internet.winsock : 2006 Jun : Re: Nailed it!

www.cryer.info
Managed Newsgroup Archive

Re: Nailed it!

Subject:Re: Nailed it!
Posted by:"Martin James" (mjames_falc..@dial.pipex.com)
Date:Sat, 24 Jun 2006 12:57:52

> Lee, Remy thanks very much. The problem is that Synchronize call blocks
the
> reading thread(premature exit situation) so the buffer gets full and the
> other app cant send more.

All hard-sync inter-thread comms,, (eg. TThread.synchronize),  where the
sender has to wait for the receiver, invites deadlocks.  To avoid this, and
improve performance, use queued comms.  When communicating to the main
thread from a secondary thread, this means PostMessage.  This is quite safe
as long as the received data is not stored in a buffer that belongs to the
thread.

You could define a class to hold your data.  As well as the actual data
buffer, (string, or array of bytes/chars), the class could have any methods
that you might use to parse your protocol.

Instantiate an instance of the class and then call read to load up the
buffer in the object.  When the read returns, call any parsing methods would
might want to call, PostMessage off the object reference and *immediately
create another one, so overwriting the object instance reference in the
thread*.  When the message is received in a main-thread message-handler, it
contains an object with data that has been 'abandoned' by the read-thread
and so can use the data easily.  Free the object after handling the data.
Since the read-thread and the main thread never operate on the same
instance, there is no chance of any clashes, eg:

------------------------------
TdataBuffer=class
private
    size:integer;
    fData:string;
    Ffield1:string;
    Ffield2:string;
    procedure parseProtocolIntoFields;
public
    property field1:string read Ffield1;
    property field2:string read Ffield2;
end;
..
procedure TreadThread.execute;
var thisBuffer:TdataBuffer;
begin
    while not Terminated do
    begin
        thisBuffer:=TdataBuffer.create;
        thisBuffer.size:=fSocket.ReadCardinal(False);
        thisBuffer.fData:= fRunSocket.ReadString(size);
        thisBuffer.parseProtocolIntoFields;
        PostMessage(fFormHandle,WM_ONDATA,0,integer(thisBuffer));
    end;
end;
..

TmyForm.WMONDATA(var message:Tmessage);
var thisData:TdataBuffer;
begin
    thisData:=TdataBuffer(message.lParam);
    try
        memo1.lines.add(thisData.field1+', '+thisData.field2);
    finally
        thisData.free;
    end;
end;
------------------------------

This method is safer than synchronize - will not deadlock.
This method is faster than synchronize - the read thread does not have to
wait for the main thread before starting the next read.
This method can be more processor-efficient than synchronize - synchronize
always generates two context-changes for each call. PM does not need to do
this.  A burst of messages from the read-thead can queue up, (perhaps
because the main thread is temporarily busy doing a big repaint), and then
the main thread can then process them in one go when it gets around to it.
This method is more flexible than synchronize, for example, instead of
freeing the object in the message-handler, as in the above example, the
object could be queued on to another secondary thread, eg one that does
logging.

TThread.stinkronize is best just avoided.

Rgds,
Martin

Replies:

In response to:

www.cryer.info
Managed Newsgroup Archive