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:"Remy Lebeau (TeamB)" (no.spam@no.spam.com)
Date:Mon, 26 Jun 2006 11:48:50

"Martin James" <mjames_falcon@dial.pipex.com> wrote in message
news:449d268a@newsgroups.borland.com...

> 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.

You also have to make sure that the main thread processes all of the queued
messages, or else memory can be leaked.  Don't just exit the message queue
and terminate the threads, without also checking for any last-minute
messages that may have been queued after the threads were signalled to
terminate but before they actually did so.

> 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.

A safer way would be to copy the data into a thread-safe container that the
main thread owns, such as a TThreadList, and then the worker thread can
signal the main thread when data has been placed into it.  When the main
thread receives the message, it can process all of the data that is
currently in the list.  This way, the main thread can process multiple data
blocks quickly since it does not have to serialize them one at a time.  For
example:

    TDataBuffer = class
    public
        constructor Create(const AData: String);
        Field1: string;
        Field2: string;
    end;

    constructor TDataBuffer.Create(const AData: String);
    begin
        // parse AData into fields as needed ...
    end;


    TMyForm = class(TForm)
    private
        procedure WMOnData(var Message: TMessage); message WMONDATA;
        //...
    public
        DataBuffer: TThreadList;
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
    end;

    constructor TMyForm.Create(AOwner: TComponent);
    begin
        inherited Create(AOwner);
        DataBuffer := TThreadList.Create;
    end;

    destructor TMyForm.Destroy;
    var
        List: TList;
        I: Integer;
    begin
        // terminate all threads, then ...
        List := DataBuffer.LockList;
        try
            for I := 0 to List.Count-1 do
                TDataBuffer(List[I]).Free;
        finally
            DataBuffer.UnlockList;
            DataBuffer.Free;
        end;
        inherited Destroy;
    end;

    procedure TMyForm.WMOnData(var Message: TMessage);
    var
        List: TList;
        Data: TDataBuffer;
    begin
        List := DataBuffer.LockList;
        try
            while List.Count > 0 do
            begin
                Data := TDataBuffer(List[0]);
                List.Delete(0);
                try
                    try
                        Memo1.Lines.Add(Data.Field1 + ', ' + Data.Field2);
                    finally
                        Data.Free;
                    end;
                except
                end;
            end;
        finally
            DataBuffer.UnlockList;
        end;
    end;

    procedure TReadThread.Execute;
    var
        Buffer: TDataBuffer;
        Data: String;
    begin
        while not Terminated do
        begin
            Data := fRunSocket.ReadString(fSocket.ReadInteger(False));
            Buffer := TDataBuffer.Create(Data);
            try
                MyForm.DataBuffer.Add(Buffer);
            except
                Buffer.Free;
                raise;
            end;
            PostMessage(FFormHandle, WM_ONDATA, 0, 0);
        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.

You have to be careful that the main thread does not slow down too much, or
else you can push data into the queue faster than it can be processed, so
the queue will fill up over time, will will lose messages, and thus will
also cause memory leaks when PostMessage() fails but the reading thread is
not cleaning up after itself.  Using a thread-safe container is safer in
that regard, as the data will not be leaked or lost, albeit in the case of
lost messages and overflowing queues, the data may not be processed for
awhile, but it will eventually.


Gambit

Replies:

In response to:

www.cryer.info
Managed Newsgroup Archive