Icon View Thread

The following is the text of the current message along with any replies.
Messages 11 to 20 of 22 total
Thread OT: Problem with threads
Sun, Nov 4 2012 11:42 PMPermanent Link

Raul

Team Elevate Team Elevate

On 11/4/2012 9:00 PM, Adam H. wrote:
> 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.

What makes you think so? I believe they are not as both rely on VCL and
forms etc - you'd need to use a OS level dialog for that (like MessageBox)

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

If you're handling the errors then your thread should free.

There is no drp file so i can't open the project but looking at the
source files i'm little confused on some aspects.

The DMThread usage is little weird to me - you're using to launch a
thread (startthread) and then thread itself creates one as well.

You're using Synhronize AND postmessage - you only need to use one or
the other (postmessage being better one).

The thread OnTerminate runs in the context of the main thread so you
should NOT be assigning it to ST.OnFinish but to TMainform handler.

i wrote a quick and dirty version of this myself and attached to binary
NG. The main form has 2 buttons - one to star the the thread and other
to stop it or it will terminate itself once the progress reaches 100.
This one assumes the trhead is created as needed and dies at the end.

I think step one is to make sure your thread runs properly and does not
call anything it should not (like any VCL commands).

Handling exceptions in threads gets tricky as you need to decide what to
do and if you need to notify user and get input you generally need to
route this thru main thread using postmessage back and forth (to ensure
fully non-blocking behaviour on both ends) and this minimally would
require some kind of state machine in the thread.

If all you want to do in the thread is to tell user you've failed then
either use Windows.MessageBox or just set a public string variable in
thread with proper message and terminate and then show the dialog in
OnTerminate handler.

Raul
Mon, Nov 5 2012 12:35 AMPermanent Link

Adam H.

Hi Raul,

Thanks for your reply.

>> 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.
>
> What makes you think so? I believe they are not as both rely on VCL and
> forms etc - you'd need to use a OS level dialog for that (like MessageBox)

Just something I read on the internet. (Oh - I can't believe I just said
that!!!!)

But I do recall somewhere someone saying that they were 'thread safe'. I
did try it in a thread and it seemed to work OK without causing
problems, but I have no need for it (well, except for throwing up error
dialogs Smiley


>> 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.
>
> If you're handling the errors then your thread should free.
>
> There is no drp file so i can't open the project but looking at the
> source files i'm little confused on some aspects.

Sorry about that. I have uploaded another one with the drp file in there
now...

> The DMThread usage is little weird to me - you're using to launch a
> thread (startthread) and then thread itself creates one as well.

I can see your confusion. The thread doesn't create another new thread -
it simply creates a datamodule (that is called DMThread. Probably very
poor choice of naming creating the confusion).

The fact that the thread is contained within the same unit as the
Datamodule is probably a little confusing. I've actually done that
deliberate as within my real application I want the one unit to contain
all the information / procedures required within it to work. This way I
can simply replace one unit and call the same command to change from
existing 'non threaded' version to a threaded version.

> You're using Synhronize AND postmessage - you only need to use one or
> the other (postmessage being better one).

Normally I have been using postmessage. I used syncronise only for the
raising of the exception (which is the latest bit I have done so far).

> The thread OnTerminate runs in the context of the main thread so you
> should NOT be assigning it to ST.OnFinish but to TMainform handler.
> i wrote a quick and dirty version of this myself

<snip>

Thanks very much for that. I've checked it out. It appears as though the
main difference between mine and yours is that you contain the thread
creation stuff within the main unit (frmmain), whereas I have it within
a separate unit.

The reason I am creating the datamodule (although probably not relivant
in this particular example) is because the datamodule contains other
components, such as TDBISamSession, DB, Tables, etc that it processes.
I've taken all these out to try and reduce it to it's most 'basic' form
at the moment.

> Handling exceptions in threads gets tricky as you need to decide what to
> do and if you need to notify user and get input you generally need to
> route this thru main thread using postmessage back and forth (to ensure
> fully non-blocking behaviour on both ends) and this minimally would
> require some kind of state machine in the thread.

For the exception - I effectively just want it to show an error dialog,
alerting the user that a problem has occurred. (Preferably the actual
error message itself), and then just close the thread down so it no
longer exists.

> If all you want to do in the thread is to tell user you've failed then
> either use Windows.MessageBox or just set a public string variable in
> thread with proper message and terminate and then show the dialog in
> OnTerminate handler.

I *think* that's what I'm doing with the application.showexception call.
After doing this, it continues with the terminate call, but for some
reason my application still things that the thread is there (although to
be fair, terminated appears to be set to 'true'), but not freed (if I'm
making sense)?

Thanks for your help!

Adam.
Mon, Nov 5 2012 8:40 AMPermanent Link

Roy Lambert

NLH Associates

Team Elevate Team Elevate

Raul



>If all you want to do in the thread is to tell user you've failed then
>either use Windows.MessageBox or just set a public string variable in
>thread with proper message and terminate and then show the dialog in
>OnTerminate handler.

Or post a message number back to the main form which then shows the appropriate error message.

Roy Lambert [Team Elevate]
Mon, Nov 5 2012 8:45 AMPermanent Link

Roy Lambert

NLH Associates

Team Elevate Team Elevate

Adam

>>> 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.
>>
>> What makes you think so? I believe they are not as both rely on VCL and
>> forms etc - you'd need to use a OS level dialog for that (like MessageBox)

As Raul says they are basically just forms. The reason they havn't caused an error YET is that they simply haven't hit the right set of circumstances.

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

Woops - raise exception.create('Break here'); is not the way to handle exceptions in threads. Use try.. except .. end eg (untested)

procedure TDMThread.DoSomething;
var
YY,XX,i : integer;
begin
XX:=0;
try
for i := 0 to 100 do
begin
if i = 17 then  YY := i / XX;
ST.progress := i;
ST.UpdateProgressBar;
sleep(20);
end;
except
PostMessage(callinghandle,yourerrorhandler,errornum,1);
Terminated := True;
end;
end;

>> The DMThread usage is little weird to me - you're using to launch a
>> thread (startthread) and then thread itself creates one as well.
>
>I can see your confusion. The thread doesn't create another new thread -
>it simply creates a datamodule (that is called DMThread. Probably very
>poor choice of naming creating the confusion).
>
>The fact that the thread is contained within the same unit as the
>Datamodule is probably a little confusing. I've actually done that
>deliberate as within my real application I want the one unit to contain
>all the information / procedures required within it to work. This way I
>can simply replace one unit and call the same command to change from
>existing 'non threaded' version to a threaded version.

I have no idea if a datamodule is thread safe or not. I ALWAYS instantiate all the database components I need within the thread, passing any needed information in as part of the constructor parameters.

The TSendThread.Execute bit of code fills me with awe.

procedure TSendThread.Execute;
var
TF : Tdmthread;
begin
try
 TF := Tdmthread.create(nil);
 TF.DoSomething;
 TF.free;
 beep; <<<<< no idea how this will get on with other threads
 Terminate; <<<< what does this refer to? If TF you've already freed it
 TF := nil;
 except
 HandleException; <<<<<<< all I can say is ouch. In theory the code will work (I think) but if my guess about the Terminate above is right it will trigger every time.
 end;
end;

I know you're trying to simplify things but if there's a way you can let me have a look at the full code I'll possibly be able to help. Alternatively I can send you the D6/DBISAM source (very old) for my email and news client which uses threads, or the D2006/ElevateDB email part of my current recruitment app both of which use threads and the Synapse library.

Roy Lambert [Team Elevate]
Mon, Nov 5 2012 8:58 AMPermanent Link

Raul

Team Elevate Team Elevate

On 11/5/2012 12:35 AM, Adam H. wrote:
> But I do recall somewhere someone saying that they were 'thread safe'. I
> did try it in a thread and it seemed to work OK without causing
> problems, but I have no need for it (well, except for throwing up error
> dialogs Smiley

AFAIK they are based on VCL and as such not thread safe. App might
compile and run still - non-thread safe does not mean there likely will
be issues later on.

> I can see your confusion. The thread doesn't create another new thread -
> it simply creates a datamodule (that is called DMThread. Probably very
> poor choice of naming creating the confusion).
That is the right way to do it :
- main thread should always create the thread(s) themselves
- inside the thread execute you create the data module(s)

> The fact that the thread is contained within the same unit as the
> Datamodule is probably a little confusing. I've actually done that
> deliberate as within my real application I want the one unit to contain
> all the information / procedures required within it to work. This way I
> can simply replace one unit and call the same command to change from
> existing 'non threaded' version to a threaded version.

That is OK as long as thread creates the DMs and main thread cannot
touch them (since they are running in the thread and not main thread).


 > The reason I am creating the datamodule (although probably not relivant
> in this particular example) is because the datamodule contains other
> components, such as TDBISamSession, DB, Tables, etc that it processes.
> I've taken all these out to try and reduce it to it's most 'basic' form
> at the moment.
Again that is exactly the way to do it as long as all interaction with
the DM is done in new thread code only.


> For the exception - I effectively just want it to show an error dialog,
> alerting the user that a problem has occurred. (Preferably the actual
> error message itself), and then just close the thread down so it no
> longer exists.
The best way to do this is to post a message to the main thread and show
message there (using either Roy's great suggestion or just store the
error message ina thread string variable and onterminate will check it
and if it's non-blank it will display it).

> I *think* that's what I'm doing with the application.showexception call.
> After doing this, it continues with the terminate call, but for some
> reason my application still things that the thread is there (although to
> be fair, terminated appears to be set to 'true'), but not freed (if I'm
> making sense)?

Application.ShowException is using showmessage which is not thread safe
so don't do that from within the thread but do it from main thread
(either postmessage event handler on from on onterminate handler).

Raul
Mon, Nov 5 2012 9:39 AMPermanent Link

Raul

Team Elevate Team Elevate

On 11/5/2012 8:45 AM, Roy Lambert wrote:
> I have no idea if a datamodule is thread safe or not. I ALWAYS instantiate all the database components I need within the thread, passing any needed information in as part of the constructor parameters.

DM is threadsafe- our main server app is 20+ threads and each thread has
1-5 DMs depending on the need.  I found the DMs really useful since you
can reuse the DM logic by creating it in multiple threads - so all my
threads above need DB access and all create their own instance of the
same DB DM.

However like you said one needs to follow the DBISAM/EDB/component X
rules for multithreaded apps to ensure unique sessions etc.

Hence the DM itself is thread safe but you still need to be thread safe
in what you do there.

Raul
Mon, Nov 5 2012 9:55 AMPermanent Link

Roy Lambert

NLH Associates

Team Elevate Team Elevate

Raul


That's a lot of datamodules Smiley

Are the 20+ threads all the same?

Roy
Mon, Nov 5 2012 12:07 PMPermanent Link

Raul

Team Elevate Team Elevate

No in this case they are all different - most of the DMs are fairly
light and provide just DBISAM access meaning Session, db etc (either for
in-mem or C/S connections).

I also have a another app where we need to do outbound TCP connections
to mobile devices and there the threads and DMs are all identical and i
use a variable pool of them to do the connection - most we've tested
with is 100 threads (and DM's) doing concurrent outbound connections at
the same time.

Funny enough one of the main problems was main thread running close to
100% at times due to worker threads using postmessage to main thread
which updated the UI - UI update is really slow in this scenario
(relatively speaking) so bottleneck was there. None of this of course
affected the worker threads.

Raul

> Raul
>
>
> That's a lot of datamodules Smiley
>
> Are the 20+ threads all the same?
>
> Roy
>
Thu, Nov 8 2012 9:50 PMPermanent Link

Adam H.

Hi Roy,

>>>> 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.
>>>
>>> What makes you think so? I believe they are not as both rely on VCL and
>>> forms etc - you'd need to use a OS level dialog for that (like MessageBox)
>
> As Raul says they are basically just forms. The reason they havn't caused an error YET is that they simply haven't hit the right set of circumstances.

OK - thanks guys. I'll have to stop believing everything I read on the
internet. <vbg>

> Woops - raise exception.create('Break here'); is not the way to handle exceptions in threads. Use try.. except .. end eg (untested)
>
> procedure TDMThread.DoSomething;
> var
> YY,XX,i : integer;
> begin
> XX:=0;
> try
> for i := 0 to 100 do
> begin
> if i = 17 then  YY := i / XX;
> ST.progress := i;
> ST.UpdateProgressBar;
> sleep(20);
> end;
> except
> PostMessage(callinghandle,yourerrorhandler,errornum,1);
> Terminated := True;
> end;
> end;

OK - so effectively there is nothing different to exceptions than
anything else - I should just trap them, and pass a message back to the
main form. The code I got was from an example of how to handle
exceptions in threads off the net. Once again - lead astray by the big
www's. ;-(

> I have no idea if a datamodule is thread safe or not. I ALWAYS instantiate all the database components I need within the thread, passing any needed information in as part of the constructor parameters.

I believe they are - and actually find it much cleaner to keep
everything on the datamodule. Just general preference I guess.

> The TSendThread.Execute bit of code fills me with awe.
>
> procedure TSendThread.Execute;
> var
> TF : Tdmthread;
> begin
> try
>    TF := Tdmthread.create(nil);
>    TF.DoSomething;
>    TF.free;
>    beep; <<<<< no idea how this will get on with other threads
>    Terminate; <<<< what does this refer to? If TF you've already freed it
>    TF := nil;
>    except
>    HandleException; <<<<<<< all I can say is ouch. In theory the code will work (I think) but if my guess about the Terminate above is right it will trigger every time.
>    end;
> end;
>
> I know you're trying to simplify things but if there's a way you can let me have a look at the full code I'll possibly be able to help. Alternatively I can send you the D6/DBISAM source (very old) for my email and news client which uses threads, or the D2006/ElevateDB email part of my current recruitment app both of which use threads and the Synapse library.

That could be difficult. There are more than 500 separate forms alone
for this particular project, not including shared units used elsewhere.
Although if you only need the code for the particular unit - I could
definitely shoot that through to you.

But once again - it's got quite a bit in there that is (IMO) not
relevant to threads which is why I'm trying to simplify what I'm doing
to only thread related issues within the example project. That way you
can at least not only see what I'm doing, but also compile and run it
too to see the same results I'm getting.

Cheers

Adam.
Thu, Nov 8 2012 9:56 PMPermanent Link

Adam H.

Hi Raul,

>> I can see your confusion. The thread doesn't create another new thread -
>> it simply creates a datamodule (that is called DMThread. Probably very
>> poor choice of naming creating the confusion).
>
> That is the right way to do it :
> - main thread should always create the thread(s) themselves
> - inside the thread execute you create the data module(s)

I think that's what I'm doing already.

1) Main thread calls a procedure in the DMThread unit that creates the
actual thread.

2) The actual thread then creates the physical TDataModule form
associated with the DMThread unit.

I could move the procedure to create the actual thread out of DMThread
and into Mainform, however I would like to avoid this where possible.

Within my applications, I create most of my forms and data modules by
calling a procedure contained within the actual unit myself. While I
think this may not be 'usual practise', I find it cleaner, in
particularly because in many parts of my application, a form can be
called up from many different units.

I figure instead of writing the same code over and over again in the
various individual units wanting to create the form, I just write the
code once - in the actual unit itself. That way there's only one line of
code to create the unit from all the other units within the project.

>> The fact that the thread is contained within the same unit as the
>> Datamodule is probably a little confusing. I've actually done that
>> deliberate as within my real application I want the one unit to contain
>> all the information / procedures required within it to work. This way I
>> can simply replace one unit and call the same command to change from
>> existing 'non threaded' version to a threaded version.
>
> That is OK as long as thread creates the DMs and main thread cannot
> touch them (since they are running in the thread and not main thread).

Excellent. That makes sense - thanks.

>   > The reason I am creating the datamodule (although probably not relivant
>> in this particular example) is because the datamodule contains other
>> components, such as TDBISamSession, DB, Tables, etc that it processes.
>> I've taken all these out to try and reduce it to it's most 'basic' form
>> at the moment.
>
> Again that is exactly the way to do it as long as all interaction with
> the DM is done in new thread code only.

Well, interaction is done either within the thread code, or within
procedures / methods that are local (private) to the actual DM that's
being created - which I think is OK, as that would still be part of that
particular thread, and not accessed from outside / anywhere else.

> The best way to do this is to post a message to the main thread and show
> message there (using either Roy's great suggestion or just store the
> error message ina thread string variable and onterminate will check it
> and if it's non-blank it will display it).

No worries - I shall go with that then.

>> I *think* that's what I'm doing with the application.showexception call.
>> After doing this, it continues with the terminate call, but for some
>> reason my application still things that the thread is there (although to
>> be fair, terminated appears to be set to 'true'), but not freed (if I'm
>> making sense)?
>
> Application.ShowException is using showmessage which is not thread safe
> so don't do that from within the thread but do it from main thread
> (either postmessage event handler on from on onterminate handler).

OK - thanks for the advise!

I'll go back when I've got a few minutes, and re-write it and try again.

Cheers

Adam.
« Previous PagePage 2 of 3Next Page »
Jump to Page:  1 2 3
Image