1

I am trying to create some email sending process. Without the multi-threading approach, it is working as expected. But it took more time to send all emails.

After I decided to use multi-threading and I also do some exception handling for the multi-threading process as well.

Here are my methods,

public enum EmailRequestProcesStatus
{
    Pending = 1,
    Processing = 2,
    Complete = 3
}
public async Task<int> InvokeSendingEmailAsync(string serverFolderPath)

{
            // update pending requests
            await UpdateEmailRequestToProcessAsync(EmailRequestProcesStatus.Processing);

            var invokedDateTime = DateTime.Now;
            // first get the CustomerEmail list
            var emailList= await GetPendingEmailList();

            IList<CustomerRule> customerRules = new List<CustomerRule>();
            int ruleLength = 0;
            List<Thread> threadList = new List<Thread>();
            // email count list
            List<int> sendEmailCountList = new List<int>();

            foreach (var customer in emailList)
            {
                ruleLength++;
                customerRules.Add(customer);
                if (ruleLength % 20 == 0)
                {
                    var custList = customerRules;
                    Thread t = new Thread(() => ProcessEmailSend(custList, serverFolderPath, invokedDateTime, sendEmailCountList));
                    threadList.Add(t);
                    t.Start();
                    customerRules = new List<CustomerRule>();
                }
            }

            if (customerRules.Count > 0)
            {
                Thread t = new Thread(() => ProcessEmailSend(customerRules, serverFolderPath, invokedDateTime, sendEmailCountList));
                threadList.Add(t);
                t.Start();
            }

            // join the all threads
            foreach (var thread in threadList)
            {
                thread.Join();
            }

            return sendEmailCountList.Count;
}

private void ProcessEmailSend(IList<CustomerRule> customerList, string serverFolderPath, DateTime invokedDateTime, List<int> sendEmailCountList)
{
   foreach(var cust in customerList)
   {
       var customerDetailList = get customerEmailDetails(cust);

       // access some file from file server by giving details
       var result = getPdf(customerDetailList.files);

       // do the rest of work using 
   }
}

private filesDto getPdf(filesDto files)
{
   try
   {
      //here I call the file server 
   }
   catch(Exception e)
   {
       UpdateEmailServiceWhenException();
       throw;
   }
}

After the throw, it will be captured by the global exception handler. If one worker thread got an exception for file not found from the file server then it inserted to the table as an exception and throw again.

From that, all the processes will be terminated and the IIS app pool also shutdown. There can be ambiguous multiple records are in the table as well.

How do I sort it?

AshanMG
  • 33
  • 9
  • 1
    app pool recycles, power cuts, all kinds of things can cause the process to suddenly exit and, even in the controlled situations, asp.net doesn't know about your threads. It'd be far better to queue the emails somewhere (database, message queue, etc) and let something not-asp-net pull email requests from that queue and dispatch the email's **reliably**. – Damien_The_Unbeliever May 11 '20 at 13:23
  • https://support.microsoft.com/en-us/help/911816/unhandled-exceptions-cause-asp-net-based-applications-to-quit-unexpect Clearly by design. – Lex Li May 11 '20 at 13:34
  • @Damien_The_Unbeliever Thank for the comment. Using queue can I gain the performance gain like having multiple threads. Without multi-threading, this error did not occur for me. – AshanMG May 11 '20 at 15:06
  • You may find this interesting: [Fire and Forget on ASP.NET](https://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html) – Theodor Zoulias May 12 '20 at 10:54

1 Answers1

2

Most likely, multiple unhandled exceptions being thrown in that worker thread results in the Rapid-Fail Protection feature.
https://learn.microsoft.com/en-us/iis/configuration/system.applicationHost/applicationPools/add/failure
Simply speaking, enough unhandled exceptions in a configurable timespan (the default is 5 in 5 minutes) will shut down the App Pool, causing the 503 Service Unavailable response.
Please check the advanced setting in the Application pool.
enter image description here
To fix this, we need to handle the exception or prevent the exception from being thrown.
Please refer to the below discussion.
Why does my IIS7 application pool shutdown after an exception in a DLL called from an ASP.NET page?

Abraham Qian
  • 7,117
  • 1
  • 8
  • 22
  • Thanks for you answer. Yes, I have caught the exception and the whole purpose of use throw keyword again is to stop the worker thread I have created and join the main thread. Is there any alternative way to do it. if so I don't wanna throw it again after catching the exception. – AshanMG May 14 '20 at 17:16