Re: Cheapest way to protect access of variables from inside a thread and from th

Giganews Newsgroups
Subject: Re: Cheapest way to protect access of variables from inside a thread and from th
Posted by:  Martin James (mjames_falc…@dial.pipex.com)
Date: Fri, 7 Oct 2005

> I have a threaded email object based on a TThread that creates a TidSMTP
and
> TidMessage.

OK, TidSMTP works fine in threads.

  The object is declared as:
>
> type
>  TThreadedEmail = class (TThread)
>
>    ThreadedEmailState        : TThreadedEmailState ;
>    ThreadedEmailResult      : TThreadedEmailResult ;
>    ThreadedEmailJobName      : ShortString ;
>    ThreadedEmailResultString : ShortString ;
>    TimeEmailStartedmS        : longword ;
>    DateTimeEmailStarted      : TDateTime ;
>
>    procedure AddAttachment (AttachmentFileID : TFilename) ;
>    procedure SendThreadedEmail ;
>    procedure AbortThreadedEmail ;
>    function  TimeSendingmS : longword ;
>
>  public
>    constructor Create (parJobName    : ShortString ;
>                        parSMTPServer  : ShortString ;
>                        parUserID      : ShortString ;
>                        parPassword    : ShortString ;
>                        parFromAddress : ShortString ;
>                        parToAddress  : ShortString ;
>                        parSubject    : ShortString ;
>                        parBody        : ANSIString ) ;

>    destructor Destroy; override ;
>
>  private
>    { Private declarations }
>
>    SMTPClient          : TidSMTP ;
>    EmailMessage        : TidMessage ;
>
>  protected
>    procedure Execute; override;
>  end;

Well, I would probably not do this - passing the message parameters in as
individual constructor parameters. I would use a seperate class to 'carry'
the parameters and also to return any results/errors etc. if required.

> The threads are all created and destroyed from the main loop and it all
> seems to work fine.

This is OK-ish if you only send occasional mails and the memory overhead of
keeping the thread/s around for the life of the app. is not worth while.

> My question - the thread itself updates the state and
> result variables and strings while it runs, but I want to be able to
display
> the thread status from the main loop.  I realise this brings into play the
> issue of two threads (one being the VCL thread) accessing a variable at
the
> same time.

Not necessarily.  You can get around this by ensuring that the SMTP thread
and the main thread never access the same variables - see later.

and I'm wondering what is the most convenient way of managing
> this - should I create a critical section with each thread and bracket the
> accesses to the variables from within the thread with the CS enter and
> leave?

This will not work - a CS is insufficient protection.

> If I do this, do I need to take any steps when accessing the
> variables from the VCL thread?  Or are the thread-safe data types likely
to
> be a more conveniet way to go?

How about this - remove all the parameters, status and results from the
thread and form:

Define a transaction class that contains everything needed to send a mail
and return status and/or results, perhaps something like this:

************************

EsmtpCommand=(EscSendMessage,EscStatus,EscTerminate);

TSmtpRequest=class
    command:EsmtpCommand;
    parJobName    : ShortString ;
    parSMTPServer  : ShortString ;
    parUserID      : ShortString ;
    parPassword    : ShortString ;
    parFromAddress : ShortString ;
    parToAddress  : ShortString ;
    parSubject    : ShortString ;
    parBody        : ANSIString
    requestingForm:TForm;
    myProgressBar:TprogressBar;
    myLogMemo:TMemo;
    statusMess:string;
    errorMess:string;
    success:boolean;
    theMessage:TidMessage;
end;
************************

Then, (either explicitly in the the main thread, or in a TSR contructor),
create and fill up one of these.  Use it as the thread constructor parameter
with the command set to EscSendMessage.  The TSR has the instances of the
form/progressBar/memo/whatever that are to receive status reports.

In the thread, whenever you need to send status info, create another
TsmtpRequest and copy in the form/progressBar/memo/whatever.  Add the status
message and set the command to EscStatus.  Then postMessage off the TSR to
the form and *forget about it in the thread*.  The SMTP thread then carries
on sending the mail.  In the message-handler of the form that gets the TSR,
you can update the  form/progressBar/memo/whatever using the info in the TSR
without any danger of the SMP thread interfering.  If the SMTP thread is
assembling a status report at that time, it does not matter because it is
using a different TSR instance.

When the mail is done, ( or has errored/excepted and filled in the errorMess
string), postMessage the 'EscSendMessage' parameter-TSR to the main thread.

This design better encapsulates the mail send function.  The SMTP thread
does not have to wait/synchronize when sending status reports and can send
as many as it wants at any time with no possiblity of any data clashes with
the main thread.  If your SMTP threads are of the 'freeOnTerminate' type,
the main thread does not have to manage anything at all.  The SMTP thread
can post off it's TSR and exit/terminate itself - it does not matter because
the thread does not contain any data shared with anything else.

Using a seperate TSR class also makes it much easier to use thread pools,
queued comms and other useful techniques.

Rgds,
Martin

Replies

None

In response to

Cheapest way to protect access of variables from inside a thread and from the VC posted by Ross McMillan on Fri, 7 Oct 2005