Icon View Thread

The following is the text of the current message along with any replies.
Messages 1 to 10 of 22 total
Thread OT: Problem with threads
Tue, Oct 30 2012 12:45 AMPermanent Link

Adam H.

Hi,

We have an unfortunate situation with one of our customers where their
SMTP server takes over 10 seconds to accept each email. As the
application I have created for them can send a number of these messages
off at a time - the users are finding that the application runs very slow.

As a work around, I am attempting to implement a thread function where
the application can call the sendmail thread, and then allow the user to
continue with whatever they want to do until the mail has been sent in
the background.

I've managed to implement the DBISam part of the application pretty
easilly. Smile

The problem I have is with the application halting whenever a dialog is
supposed to appear until the thread is completed. I've tried
synchronise, postmessages, etc - but can't seem to get this to work.

If anyone is able to help - I have put an example of my project in the
Binary section of the DBISam newsgroup under [ThreadProject].  I have
eliminated all DBISam code out - so I know that DBISam is not the issue
here, but was hoping someone with thread knowledge may be able to assist
or point where I've gone wrong? Smile

Cheers

Adam.
Tue, Oct 30 2012 5:17 AMPermanent Link

Roy Lambert

NLH Associates

Team Elevate Team Elevate

Adam


Looking at that bit of code you seem to be committing most of the sins in the book. I don't know how much is simple trying things out so I'll be ruthless <vbg>

This chunk

while not ST.Terminated do
application.processmessages;

essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc

Here

 TF := TFormThread.create(nil);

you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up.

This bit in your post <<The problem I have is with the application halting whenever a dialog is supposed to appear until the thread is completed.>> if I'm reading it correctly is something I'm not clear on - is this dialog for information presentation of for the user to enter information?

Getting progress information back from a thread isn't difficult, however, the way a lot of people use - synchronise - is dire. It essentially blocks the main thread (ie the app) until synchronisation is done.

What you need to do is:

1. In your thread definition add two extra parameters onto the constructor - respondmessage and respondhandle. These will be a message to be used in the WM_USER block and the handle of the form that will process the response message.

2. Inside the thread have a postmessage (NOT sendmessage that waits until it gets a response) send a message using respondmessage and respondhandle and set wParam and lParam to reflect whatever the progress is (eg have a counter internal to the thread which is implemented at the appropriate point and pass that back)

3. In the form that will process the progress message have a message handler eg   procedure EmailsHaveBeenSkipped(var Message: TMessage); In that procedure you do whatever you want - display a dialog, update a progress bar etc

If you use the above approach then its "easy". The only potential problem is that the Windows handle for the form processing the progress request gets changed. I implemented MainHWND := AllocateHWND(MainHWNDHandler); but this means that I then have to have a little procedure to process all messages -

procedure TMainForm.MainHWNDHandler(var msg: TMessage);
begin
case msg.Msg of
 ProgressReport: FilterResult(msg);
 jmpRequired: ProcessJumpInstruction(msg);
 StatusMessage: ChangeStatusMessage(msg);
 PinToJump: AddToJumpButton(msg);
 OpenViewHistory: OpenViewHistoryForm(msg);
 SubsidForm: MakeSubsidiaryForm(msg);
 OpenMainForm: RemoteOpenMainForm(msg);
 InitialLogon: DoInitialLogon(msg);
 CloseSubsidTab: CloseNonMainTab(msg);
 StartPhoneTimer: DialedStartStopwatch(msg);
 StopPhoneTimer: DialedStopStopwatch(msg);
 AlarmsRunning: SetAlarmRunningStatus(msg);
 AlarmThreadCrash: AlarmsNotWorking(msg);
 CurrentAlarmCheck: FlipAniStatus(msg);
 AskForJmpVars: DoAskForJmpVars(msg);
 NewEMailReceived: if IsCreated[tagEMails] then PostMessage(frmHndl[tagEmails], bgMsgRefresh, msg.WParam, msg.LParam);
 OpenTfRMailViewer: ViewAttachedEMail;
 EMailsSkipped: EmailsHaveBeenSkipped(msg);
else DefWindowProc(Self.Handle, msg.Msg, msg.wParam, msg.lParam);
end;
end;

If you want to interact with the thread and pass information in from a dialog that's a bit more complex so I won't go into it unless you need it.

Hopefully the above will get you going in the right direction. If not then ask more questions Smiley

Roy Lambert [Team Elevate]
Tue, Oct 30 2012 8:02 PMPermanent Link

Adam H.

Hi Roy,

> Looking at that bit of code you seem to be committing most of the sins in the book. I don't know how much is simple trying things out so I'll be ruthless <vbg>

I have no problems with being ruthless. As I mentioned, I have little
understanding about threads - so I'm thankful for your help! Smile

 > This chunk
>
> while not ST.Terminated do
> application.processmessages;
>
> essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc

So effectively I should just not worry about it at all? I was just
wondering how I go about Freeing and making ST nil again once the thread
is complete. Can I do that in the thread itself. (After calling terminate)?

> Here
>
>    TF := TFormThread.create(nil);
>
> you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up.

So I should have created a Datamodule instead.

I've just made this change, and it's already working a ton better!

> This bit in your post <<The problem I have is with the application halting whenever a dialog is supposed to appear until the thread is completed.>> if I'm reading it correctly is something I'm not clear on - is this dialog for information presentation of for the user to enter information?

The dialog that appears should be called by the main thread, not the new
thread I'm creating. Effectively all I'm wanting to do with the new
thread is process something, and show a process bar of where it is up to.

The problem I was having was that once a thread was running, if a user
clicked on the "SHOW A MESSAGE" button, the application would
effectively stop working completely.

> Getting progress information back from a thread isn't difficult, however, the way a lot of people use - synchronise - is dire. It essentially blocks the main thread (ie the app) until synchronisation is done.
>
> What you need to do is:

<snip>

Thanks Roy! I'll need a bit of time to digest the rest of what you have
just said, but will go through it and see what I can come up with.

Once again - thanks very much for taking the time out to help!

> If you want to interact with the thread and pass information in from
> a dialog that's a bit more complex so I won't go into it unless you
> need it.

Thanksfully this isn't required at this stage. The simpler I can keep
things, the better. Smile

> Hopefully the above will get you going in the right direction. If not
> then ask more questions Smiley

It has certainly helped a lot. Don't worry - I'll be back with more
questions if I get stuck.

Thanks again for your assistance!

Cheers

Adam.


> 1. In your thread definition add two extra parameters onto the constructor - respondmessage and respondhandle. These will be a message to be used in the WM_USER block and the handle of the form that will process the response message.
>
> 2. Inside the thread have a postmessage (NOT sendmessage that waits until it gets a response) send a message using respondmessage and respondhandle and set wParam and lParam to reflect whatever the progress is (eg have a counter internal to the thread which is implemented at the appropriate point and pass that back)
>
> 3. In the form that will process the progress message have a message handler eg   procedure EmailsHaveBeenSkipped(var Message: TMessage); In that procedure you do whatever you want - display a dialog, update a progress bar etc
>
> If you use the above approach then its "easy". The only potential problem is that the Windows handle for the form processing the progress request gets changed. I implemented MainHWND := AllocateHWND(MainHWNDHandler); but this means that I then have to have a little procedure to process all messages -
>
> procedure TMainForm.MainHWNDHandler(var msg: TMessage);
> begin
> case msg.Msg of
>    ProgressReport: FilterResult(msg);
>    jmpRequired: ProcessJumpInstruction(msg);
>    StatusMessage: ChangeStatusMessage(msg);
>    PinToJump: AddToJumpButton(msg);
>    OpenViewHistory: OpenViewHistoryForm(msg);
>    SubsidForm: MakeSubsidiaryForm(msg);
>    OpenMainForm: RemoteOpenMainForm(msg);
>    InitialLogon: DoInitialLogon(msg);
>    CloseSubsidTab: CloseNonMainTab(msg);
>    StartPhoneTimer: DialedStartStopwatch(msg);
>    StopPhoneTimer: DialedStopStopwatch(msg);
>    AlarmsRunning: SetAlarmRunningStatus(msg);
>    AlarmThreadCrash: AlarmsNotWorking(msg);
>    CurrentAlarmCheck: FlipAniStatus(msg);
>    AskForJmpVars: DoAskForJmpVars(msg);
>    NewEMailReceived: if IsCreated[tagEMails] then PostMessage(frmHndl[tagEmails], bgMsgRefresh, msg.WParam, msg.LParam);
>    OpenTfRMailViewer: ViewAttachedEMail;
>    EMailsSkipped: EmailsHaveBeenSkipped(msg);
> else DefWindowProc(Self.Handle, msg.Msg, msg.wParam, msg.lParam);
> end;
> end;
>
>
> Roy Lambert [Team Elevate]
>
Tue, Oct 30 2012 8:02 PMPermanent Link

Adam H.

Hi Roy,

> Looking at that bit of code you seem to be committing most of the sins in the book. I don't know how much is simple trying things out so I'll be ruthless <vbg>

I have no problems with being ruthless. As I mentioned, I have little
understanding about threads - so I'm thankful for your help! Smile

 > This chunk
>
> while not ST.Terminated do
> application.processmessages;
>
> essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc

So effectively I should just not worry about it at all? I was just
wondering how I go about Freeing and making ST nil again once the thread
is complete. Can I do that in the thread itself. (After calling terminate)?

> Here
>
>    TF := TFormThread.create(nil);
>
> you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up.

So I should have created a Datamodule instead.

I've just made this change, and it's already working a ton better!

> This bit in your post <<The problem I have is with the application halting whenever a dialog is supposed to appear until the thread is completed.>> if I'm reading it correctly is something I'm not clear on - is this dialog for information presentation of for the user to enter information?

The dialog that appears should be called by the main thread, not the new
thread I'm creating. Effectively all I'm wanting to do with the new
thread is process something, and show a process bar of where it is up to.

The problem I was having was that once a thread was running, if a user
clicked on the "SHOW A MESSAGE" button, the application would
effectively stop working completely.

> Getting progress information back from a thread isn't difficult, however, the way a lot of people use - synchronise - is dire. It essentially blocks the main thread (ie the app) until synchronisation is done.
>
> What you need to do is:

<snip>

Thanks Roy! I'll need a bit of time to digest the rest of what you have
just said, but will go through it and see what I can come up with.

Once again - thanks very much for taking the time out to help!

> If you want to interact with the thread and pass information in from
> a dialog that's a bit more complex so I won't go into it unless you
> need it.

Thanksfully this isn't required at this stage. The simpler I can keep
things, the better. Smile

> Hopefully the above will get you going in the right direction. If not
> then ask more questions Smiley

It has certainly helped a lot. Don't worry - I'll be back with more
questions if I get stuck.

Thanks again for your assistance!

Cheers

Adam.
Wed, Oct 31 2012 4:11 AMPermanent Link

Roy Lambert

NLH Associates

Team Elevate Team Elevate

Adam

>I have no problems with being ruthless. As I mentioned, I have little
>understanding about threads - so I'm thankful for your help! Smile

Ok goody - carte blanch - so to start - I hope your hiccoughs are better soonSmiley

> > This chunk
>>
>> while not ST.Terminated do
>> application.processmessages;
>>
>> essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc
>
>So effectively I should just not worry about it at all? I was just
>wondering how I go about Freeing and making ST nil again once the thread
>is complete. Can I do that in the thread itself. (After calling terminate)?


Threads have a property which can be set in their constructor or the Execute method

FreeOnTerminate := True;

which takes care of freeing the thread if you're not going to re-use it. I use the constructor.

>> Here
>>
>> TF := TFormThread.create(nil);
>>
>> you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up.
>
>So I should have created a Datamodule instead.
>
>I've just made this change, and it's already working a ton better!

Good, but treat it just like any other class and use a unit instead. You can even define it in the form that uses it - after all its just another class declaration.


>Thanks Roy! I'll need a bit of time to digest the rest of what you have
>just said, but will go through it and see what I can come up with.
>
>Once again - thanks very much for taking the time out to help!

If you need them I can send you a couple of fully working thread units. They won't compile at your end because they're calling all sorts of crap.

Roy Lambert [Team Elevate]
Wed, Oct 31 2012 7:35 AMPermanent Link

John Hay

Adam,

I know that synchronize has been rightly lambasted in the past, but it was improved in Delphi 6 (using postmessage
instead of sendmessage) and there is now the option to use TThread.Queue (since Delphi 2006).

I have found and posted a simplistic use of a thread updating a listbox that I wrote when I was first trying to
understand threads.

John


Wed, Oct 31 2012 8:09 AMPermanent Link

Roy Lambert

NLH Associates

Team Elevate Team Elevate

John

>I know that synchronize has been rightly lambasted in the past, but it was improved in Delphi 6 (using postmessage
>instead of sendmessage) and there is now the option to use TThread.Queue (since Delphi 2006).

Most of the negatives I've seen posted about it relate to the fact that it "stalls" the main thread whilst synchronisation is occurring. Send rather than Postmessage would account for that.

>I have found and posted a simplistic use of a thread updating a listbox that I wrote when I was first trying to
>understand threads.

Interesting little example - not sure about the formatting - it looks worse than Delphi's Smiley

Roy
Wed, Oct 31 2012 8:19 AMPermanent Link

John Hay

Roy
> Most of the negatives I've seen posted about it relate to the fact that it "stalls" the main thread whilst
synchronisation is occurring. Send rather than Postmessage would account for that.

Might be worth reading

http://stackoverflow.com/questions/1806339/is-it-better-to-use-tthreads-synchronize-or-use-window-messages-for-ipc-betwe

> Interesting little example - not sure about the formatting - it looks worse than Delphi's Smiley

Project1,Unit1,Form1,Buton1,Button2 etc - clear as mud Smiley

John

Wed, Oct 31 2012 9:31 AMPermanent Link

Roy Lambert

NLH Associates

Team Elevate Team Elevate

John

>Might be worth reading
>
>http://stackoverflow.com/questions/1806339/is-it-better-to-use-tthreads-synchronize-or-use-window-messages-for-ipc-betwe

Quite interesting especially the bit about using SendMessage to achieve "synchronisation".

My world view is that you should only be bothered about "thread has started", "thread has finished" but I'm happy with "we've reached this point" but the minute you start having to pass more data than that I start to think it should be done in the main thread. I like some sort of visual indicator showing that the thread is still alive (often a progress bar) but I do consider that threads should be kicked out into the cold and allowed to get on with their own life!

Roy
Sun, Nov 4 2012 9:00 PMPermanent Link

Adam H.

Hi Roy and John,

I have made some considerable progress since our last discussion thanks
to your help! Thanks very much!

There are no more application.processmessages and calls back so far are
done via postmessages. Smile

One thing I also learned that surprised me was that showmessage and
messagedlg are thread safe - so I can actually raise one if I want
within a thread for user intervention.

However - my current situation has me wondering how best to handle
exceptions within threads.

I have gotten to a stage after searching the net where I am handling
exceptions, but it's not freeing up the thread once it's completed. As a
result - the application still things the thread is running, and since I
don't want to close the app until threads have been completed (to avoid
corrupting any data on writes) I'm not sure where to go to from here.

I'm sure in this instance it's probably just one or two lines I've got
wrong, but if someone is willing to take a look at my updated example, I
would be forever grateful. Smile

I've whacked a copy of the update in the binaries NG, or alternatively
from
http://www.elevatesoft.com/forums?action=attach&category=dbisam&id=dbisam_binaries&msg=1803&attach=1


Thanks again for all the assistance!

Cheers

Adam.





On 31/10/2012 7:11 PM, Roy Lambert wrote:
> Adam
>
>> I have no problems with being ruthless. As I mentioned, I have little
>> understanding about threads - so I'm thankful for your help! Smile
>
> Ok goody - carte blanch - so to start - I hope your hiccoughs are better soonSmiley
>
>>> This chunk
>>>
>>> while not ST.Terminated do
>>> application.processmessages;
>>>
>>> essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc
>>
>> So effectively I should just not worry about it at all? I was just
>> wondering how I go about Freeing and making ST nil again once the thread
>> is complete. Can I do that in the thread itself. (After calling terminate)?
>
>
> Threads have a property which can be set in their constructor or the Execute method
>
> FreeOnTerminate := True;
>
> which takes care of freeing the thread if you're not going to re-use it. I use the constructor.
>
>>> Here
>>>
>>> TF := TFormThread.create(nil);
>>>
>>> you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up.
>>
>> So I should have created a Datamodule instead.
>>
>> I've just made this change, and it's already working a ton better!
>
> Good, but treat it just like any other class and use a unit instead. You can even define it in the form that uses it - after all its just another class declaration.
>
>
>> Thanks Roy! I'll need a bit of time to digest the rest of what you have
>> just said, but will go through it and see what I can come up with.
>>
>> Once again - thanks very much for taking the time out to help!
>
> If you need them I can send you a couple of fully working thread units. They won't compile at your end because they're calling all sorts of crap.
>
> Roy Lambert [Team Elevate]
>
Page 1 of 3Next Page »
Jump to Page:  1 2 3
Image