Basic Mutlithreading Questions

J

JJ

I've done a little multi-threading on winform apps some time ago now, so I'm
not a complete beginner, but best assume I am for any explanations..:

This is an asp.net 2.0 website, using c#:
I have a thread whose job is to send several emails (i.e. a long task that
mustn't be run by more that one user at a time).
It all works fine, but I am concerned about the lifetime of a thread in the
case of smtp errors, and how it will work with several users trying to run
the same process.

The thread is started like:
ParameterizedThreadStart pts = new ParameterizedThreadStart(SendEmails);
Thread thread = new Thread(pts);
thread.Name = "SendEmails";
thread.Priority = ThreadPriority.BelowNormal;
thread.Start(parameters);

Several static variables are written to by the main calling code (the main
app) and then by the 'SendEmails' thread, in the following manner:

Lock.AcquireWriterLock(Timeout.Infinite);
Email.SentMails = 0;
Email.IsSending = true;
Lock.ReleaseWriterLock();

I am therefore assuming that if another user is viewing the pages and tries
to run the email sending process, I can check to see if the Email.IsSending
is set and if it is, don't allow the 'SendEmails' thread to be run and
present a 'busy' page. Does this sound correct?

My questions are:
1. If there are some unexpected errors in the SendEmails thread, would the
thread ever end? Should threads always be started with some sort of
timeout - of so how ? Should the thread's code always be in some sort of
try..catch ?

2. To do actions on the database only when the thread finishes, (i.e. using
Thread.Wait()), I assume I have to start a main calling thread first, which
itself will wait for the SendEmails thread to finish before it does its job?

3. How can you check if a particular thread is running, whichever user it
was started by, so I don't accidentally try to start another one? (is my
approach with setting a static flag for a particular thread - in this case
'IsSending' - the best approach?

4. If I was to have a 'Cancel Sending' button, is there any cleaning up or
waiting I need to do (apart from resetting Email.IsSending = false), or is
it just simply Thread.Abort?

Sorry for the numerous questions,

JJ
 
R

Rick Strahl [MVP]

Hi JJ,

My questions are:
1. If there are some unexpected errors in the SendEmails thread, would the
thread ever end? Should threads always be started with some sort of
timeout - of so how ? Should the thread's code always be in some sort of
try..catch ?

Yes. The Exception will kill the thread. Your best bet is to ensure that
that doesn't happen by wrapping your mainline code into exception handlers.

2. To do actions on the database only when the thread finishes, (i.e.
using Thread.Wait()), I assume I have to start a main calling thread
first, which itself will wait for the SendEmails thread to finish before
it does its job?

Look into using WaitHandles (AutoResetEvent works great for this). Basically
you can use these WaitHandles to make a thread wait until another thread
triggers it.

I recently posted a blog entry about creating a basic Scheduler in ASP.NET
here and it demonstrates how you can do this:
http://west-wind.com/weblog/posts/67557.aspx

The code's not quite generic, but close and might give you an idea how you
can run a 'background' thread in ASP.NET whether you're launching it on app
start (as the post does) or whether you explicitly launch it from a specific
page hit.
3. How can you check if a particular thread is running, whichever user it
was started by, so I don't accidentally try to start another one? (is my
approach with setting a static flag for a particular thread - in this case
'IsSending' - the best approach?

You can't easily do that at least not reliably. Your best bet is to create a
scheduler that runs - does what it needs to and goes to sleep until the next
request is submitted.

4. If I was to have a 'Cancel Sending' button, is there any cleaning up or
waiting I need to do (apart from resetting Email.IsSending = false), or is
it just simply Thread.Abort?

Typically you shouldn't call Thread.Abort() as it is a hard reset and
doesn't clean up resources. A better approach for you is probably to check
on every iteration (ie. after each email that's been sent) if the thread
should be cancelled and then simply exit the loop and or method you're
running in.

+++ Rick ---
 
J

JJ

Hi Rick,

Thanks for you information and a great article. I do have some other
questions based on the info though:

Thanks for clearing up the 'Cancel thread' bit - setting a flag and exiting
the loop is a lot cleaner.

My confusion seems to be around two areas (in comparison with winform apps):
1. What the scope of threads are when it comes to different users accessing
the website;
[ i.e. are the main website pages/classes all running on ONE thread and
being accessed by multiple users, or does each user that accesses the
website start a new separate thread on the webserver?]
2. The effect of waiting for a thread to complete, based on the above
confusion;
[i.e. does the whole website freeze if the main calling code waits for a
thread to complete? Does it freeze for just the user that started the
'SendEmails' thread or for the entire website for all users?]

My concern also is not so much keeping the 'SendEmails' thread alive, as
much as making sure I don't keep spawning new threads or keeping the thread
alive indefinately because of no timeout and some sort of sending hangup on
the smtp server. Clearly I can't restart a shared web server, like I could
just exit a winform app and all its spawned threads.

Your example has a thread running indefintately - there are reasons why I
don't want to do this - one being that this is only one small function of
the website that may not always be used, so it seems in appropriate to keep
the thread running, and more appropriate to only run it when it is needed.

If I was to use a WaitHandle, which looks similar to the Thread.Wait
approach, in order to have some sort of failsafe timeout and to only update
data when the 'SendEmails' thread has done its job, wouldn't I still need to
do this waiting in new a calling thread (the 'MainSendEmailsThread' thread
that in turn calls the 'SendEmails' thread )?

My thinking being that I wouldn't want the main code to stop and wait -
doesn't this stop other users accessing that class (or the website) too (see
my 'confusion' explanation above)? Having a 'MainSendEmailsThread' and a
'SendEmailsThread' would mean that the 'MainSendEmailsThread' would just set
the 'IsSending' flag then call the 'SendEmailsThread' and wait for its
completion, before doing any tidying up or data updating. That is, the
thread that is 'frozen' whilst waiting is the 'MainSendEmailsThread'
and not the main website code, hence allowing that user and others to
continue viewing pages whilst the two threads are running.

In effect, both threads would only have one instance running as a check
would be made each time a user clicks the 'Send Emails' button to see if the
'IsSending' flag is set. If it is, then the user would be informed the the
function is 'busy', and no threads would be started by that user.

Is this all based on entirely the wrong assumption about asp.net threads, or
am I on the right approach for this senario?

Thanks in advance,

JJ
 
A

Alvin Bruney [MVP]

I have a thread whose job is to send several emails (i.e. a long task that
mustn't be run by more that one user at a time).
It all works fine, but I am concerned about the lifetime of a thread in
the case of smtp errors, and how it will work with several users trying to
run the same process.

Email servers are async in nature, you are forcing a queue mechanism. This
is bad, very bad.
Another thing, did I mention that this was bad?
Several static variables are written to by the main calling code (the main
app) and then by the 'SendEmails' thread, in the following manner:
Bad. very bad. Your main application will essential block your calling
threads with the writer lock. If the writer lock chokes, you don't have an
exception block, your application will hang. Even if it doesn't hang, it
will slow to a crawl as load increases.

Additionally, you sit and wait for a lock for how ever long it takes. Let's
say it takes 23 minutes to acquire that lock, your user is sitting there for
23 minutes looking at a busy page. So they close the browser and come back
in again, the lock is still there. But now, two requests are waiting on a
lock. Lock set to infinite time out - bad idea.

1. If there are some unexpected errors in the SendEmails thread, would the
thread ever end? Should threads always be started with some sort of
timeout - of so how ? Should the thread's code always be in some sort of
try..catch ?

I think you know the answers to those questions.

2. To do actions on the database only when the thread finishes, (i.e. using
Thread.Wait()), I assume I have to start a main calling thread first, which
itself will wait for the SendEmails thread to finish before it does its job?

No, you can synchronize the threads which will essential mirror a wait
behavior. There are some wait states that are more efficient than others so
you may want to look at those. However, this is the least of your problems.

3. How can you check if a particular thread is running, whichever user it
was started by, so I don't accidentally try to start another one? (is my
approach with setting a static flag for a particular thread - in this case
'IsSending' - the best approach?

No. When you start a thread, don't throw away the handle to the thread. Keep
it, so you can use it to inquire about the state of the thread. In that
case, it is variable thread.IsRunning (if memory serves me correctly)

4. If I was to have a 'Cancel Sending' button, is there any cleaning up or
waiting I need to do (apart from resetting Email.IsSending = false), or is
it just simply Thread.Abort?

Here is the thing with thread.abort. Say you have a moving car and you want
to bring it to a halt, you can drive it into a brick wall to stop it, or you
can use the brake pedal. Both get the job done, one does so more *elegantly
than the other. Use a flag to signal your thread to stop.



--
Regards,
Alvin Bruney
------------------------------------------------------
Shameless author plug
Excel Services for .NET is coming...
https://www.microsoft.com/MSPress/books/10933.aspx
OWC Black Book www.lulu.com/owc
Professional VSTO 2005 - Wrox/Wiley
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,995
Messages
2,570,231
Members
46,820
Latest member
GilbertoA5

Latest Threads

Top