Preventing a multithreaded website from having too many resources

I have built a mass mailing website to send to a client who has to send 80,000 emails in one message. It basically creates a new thread to send so that control can be pushed back to the UI (so the feedback page can load) and then a new thread is created for each company to send emails to their recipients. All emails are sent to the queue using this code:

// Loop through the companies and send their mail to the specified recipients
        // while creating a new thread for each company
        // A new thread is started so that the feedback page can load
        SendingThread = Task.Factory.StartNew(() =>
        {
            // This is a thread safe for loop
            Parallel.ForEach<CompanyEntity>(companies, company =>
            {
                    // Start a new thread for each company send
                    Task.Factory.StartNew(() =>
                    {
                        // Get the recipients for this company
                        var companyRecipients = GetSubscribersForCompany(company.Id, recipients);

                        // Send the newsletter to the company recipients
                        var success = SendNewsletterForCompany(newsletter, company, companyRecipients, language,
                                                               version, company.AdvertCollectionViaNewsletterCompanyAdvertLink, newsletter.NewsletterType, email);

                        // Add the status update so the front end can view a list of updated conpany statuses
                        if (success)
                            AddStatusUpdate(company.CompanyTitle + " has completed processing.");

                        // Starts sending the emails if the engine hasn't already been started
                        SendEngine.Start(CurrentSmtpClient, this);

                    }).ContinueWith(antecendent => EndCompaniesSendUpdate(companiesToProcess, companiesProcessed), TaskContinuationOptions.OnlyOnRanToCompletion);
            });
        }, new CancellationToken(), TaskCreationOptions.LongRunning, TaskScheduler.Default);

      

While the emails are queued, the sending engine takes over and pulls emails from the queue and then sends them using the new Parallel class:

Action action = () =>
        {
            MailMessage message;
            while (queue.TryDequeue(out message))
            {
                SendMessage(sendingServer, message, factory);
            }
        };

        // Start 5 concurrent actions to send the messages in parallel.
        Parallel.Invoke(action, action, action, action, action);

      

This all works great and can send 40,000 newsletters in 10 minutes. The only problem is that the RAM and CPU on the server are consumed 100% in those 10 minutes. This affects other sites on the server as they cannot be accessed.

Is there a way to restrict the sending application's resource usage either in IIS 7.5 or by modifying the code above?

+3


source to share


1 answer


Problems:

  • You spawn a thread inside Parallel ForEach, the "Parallel" part means it already spawns a thread for the body. You are embedding Parallel Invoke instead of Action inside Parallel ForEach inside another Action.

  • You run a while loop inside a thread with no rest for the CPU. This is a 5x Parallel Call.

Answers:

To use the CPU, you need to give the recycling a breather. While in a TryDequeue loop, take a short nap.



        MailMessage message;
        while (queue.TryDequeue(out message))
        {
            SendMessage(sendingServer, message, factory);
            Thread.Sleep(16);
        }

      

For RAM and CPU usage, you need to process LESS immediately.

        SendingThread = Task.Factory.StartNew(() =>
        {
            foreach(var company in companies) 
            {
                        // Get the recipients for this company
                        var companyRecipients = GetSubscribersForCompany(company.Id, recipients);

                        // Send the newsletter to the company recipients
                        var success = SendNewsletterForCompany(newsletter, company, companyRecipients, language,
                                                               version, company.AdvertCollectionViaNewsletterCompanyAdvertLink, newsletter.NewsletterType, email);

                        // Add the status update so the front end can view a list of updated conpany statuses
                        if (success)
                            AddStatusUpdate(company.CompanyTitle + " has completed processing.");

                        // Starts sending the emails if the engine hasn't already been started
                        SendEngine.Start(CurrentSmtpClient, this);


            }
       }, new CancellationToken(), TaskCreationOptions.LongRunning, TaskScheduler.Default);

      

+2


source







All Articles