Question

i am trying to build a small email worker that sends my emails. The email worker operates in its own thread. The global.asax starts the thread and then it runs for one round before throwing this exception. I have been trying to find out where it throws it, but it seems to be different each time. The records are printed to the text file so that works, maybe the dispose functions?

This is the exception thrown:

Starting a new round in the email worker for-loop
Going to sleep in the email worker for-loop
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code

This is the code for the EmailWorker

public static class EmailWorker
{
    public static void Work()
    {
        TimeSpan sleepTime = new TimeSpan(0, 1, 0);

        for (; ; )
        {
            Debug.WriteLine("Starting a new round in the email worker for-loop");
            try
            {
                // Get the records
                CS_Code.UtopiaDataContext db = new CS_Code.UtopiaDataContext(); 
                List<CS_Code.Global_Email> emailsToSend = (from xx in db.Global_Emails select xx).ToList();


                // Write the records to a file (for now)
                TextWriter writer = new StreamWriter(@"test.txt", true); // For debugging
                foreach (var email in emailsToSend)
                    writer.WriteLine("To: " + email.Email_Address + ", Subject: " + email.Subject + ", uid:" + email.UserName.ToString());
                writer.Close();
                writer.Dispose();

                // Delete the used records from the database
                foreach (var email in emailsToSend)
                    db.Global_Emails.DeleteOnSubmit(email);
                db.SubmitChanges();
                db.Dispose();


                Debug.WriteLine("Going to sleep in the email worker for-loop");
                Thread.Sleep(sleepTime); // Sleep for 1 minute.
                Debug.WriteLine("Just woke up in the email worker for-loop");
            }
            catch (Exception e)
            {               
                Debug.WriteLine(e.Message);
                break;
            }
        }
    }
}

This is the global.asax file that starts everything:

private static Thread EmailWorkerThread { get; set; }

void Application_Start(object sender, EventArgs e)
{
    // Email worker thread
    if ((EmailWorkerThread == null) || (!EmailWorkerThread.IsAlive))
    {
        ThreadStart ts = new ThreadStart(EmailWorker.Work);
        EmailWorkerThread = new Thread(ts);
        EmailWorkerThread.Start();
    }
}

void Application_End(object sender, EventArgs e)
{
    // Email worker thread
    if ((EmailWorkerThread != null) || (EmailWorkerThread.IsAlive))
        EmailWorkerThread.Abort();
    EmailWorkerThread = null;
}
Was it helpful?

Solution

The thread that you are creating is being aborted by asp.net. Creating long running threads are a major no-no in asp.net.

A we have a working workaround, that we are not proud of... Create a timer that process pending emails.

static Timer processTimer; // declare static at service level

// At the static constructor:
processTimer = new Timer(new TimerCallback(TimerTick), 60000, 10000, defaultInterval); //wait a minute before begin (dueTime = 60000)

To be extra carefull, at the Timer_Tick event, handle at most N emails, so the timer tick ends, and a new one rises...

This is working on our production environment...

OTHER TIPS

You need to move the EmailWorker out of ASP.NET and into windows service. You can communicate between your webpage and the service via WCF. ASP.NET is not meant for long running tasks.

The ThreadAbortException occurs when your application shuts down and you call EmailWorkerThread.Abort(). This is expected behavior and it occurs in random places because the code can be in different "spots" when the Application_End calls Abort() on the thread.

As per MSDN, the ThreadAbortException is a special exception that is always rethrown even after being handled by your code. It's intended to let you know that the thread is shutting down to give you an opportunity to clean up.

You application is likely shutting down because IIS is shutting down the worker process after a set amount of idle time, or some other imposed limitation. Again, this is to be expected, ASP.NET applications tend to have "limited lifetimes", so long running threads like this are not really a good idea.

I'm not sure what your exact question was, but hopefully I answered it.

The child thread can only live as long as the parent thread is alive. Even though you've got a thread defined in what appears to be the application level, I can't imagine this thread actually survives outside of the HttpRequest thread. To me this means that the moment your request thread terminates, your child EmailWorkerThread terminates. Since the thread is sleeping, the next line attempts to execute but it can't because the HttpRequest thread has already run its course.

ThreadAbortException as the name implies, is thrown when a thread is working and is aborted. This happens at this place:

 EmailWorkerThread.Abort();

It cannot be caught and it happens when you application ends.

A better implementation would be to replace the infinite loop with cancelable loop.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top