获取多线程以在网页和业务层之间进行同步

时间:2013-03-20 02:46:48

标签: c# asp.net backgroundworker thread-synchronization iasyncoperation

我遇到一个问题,让这块代码工作(抱歉长度)。该代码经过消毒,只是显示诊断问题的相关部分。

从控制台应用程序运行时,它工作正常。 但是当从ASP.NET应用程序调用Utlities.SendBulkEmail时, BulkEmailCompleted例程永远不会触发,而这个例程会增加m_CompletedWorkers计数器。

如何重构SendBulkEmail例程以使用AsyncOperationManager而不是BackgroundWorker,因此我可以保证线程返回结果。

SendBulkEmail例程本身不是多线程的。多线程发生在它的foreach循环中。

我认为原始代码的基础来自本网站: http://www.dotnetfunda.com/articles/article613-background-processes-in-asp-net-web-applications.aspx

Utilities项目在各种解决方案中共享,并且几乎是独立的。

我希望我明白这一点。

任何帮助将不胜感激。

代码如下:

在网站项目中(Control.ascx.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Configuration;
using <company>.BusinessObjects.Emails;
using <company>.DataAccessors;
using <company>.DataObjects;
using <company>.Enumerations;
using <company>.Interfaces;
using <company>.Utilities;
    ...
        protected void sendButton_OnClick(object sender, EventArgs e)
        {
            ...
                if (HasBenefits)
                {
                    ReportingEmails emailer = new ReportingEmails();
                    ...
                    //Prevent send if nothing selected
                    if (... > 0)
                    {
                        List<EmailOutcome> results = emailer.GenerateNotificationEmailsForEmployer(<some int>, <some list>);
                        ...
                    }
                }
            ...
        }

在商业对象项目中

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Mail;
using System.Resources;
using System.Text;
using System.IO;
using <company>.BusinessObjects.Emails.Helpers;
using <company>.DataAccessors;
using <company>.DataObjects;
using <company>.Enumerations;
using <company>.Utilities;

namespace <company>.BusinessObjects.Emails
{  
    public class ReportingEmails
    {   
        ...
        public List<EmailOutcome> GenerateNotificationEmailsForEmployer(int employerID, List<int> benefits = null)
        {
            ...
            SendNotificationEmails(List<ReportData>, ref emailSuccessByBenefit, true, benefitsToExclude);
            return emailSuccessByBenefit;
        }

        private void SendNotificationEmails(List<ReporterCommsData> reportData, ref List<EmailOutcome> emailSuccessByBenefit, bool isFleet, List<int> benefitsToExclude)
        {
            Dictionary<int, MailMessage> bulkEmails = new Dictionary<int, MailMessage>();

            //build up the set of emails to send
            foreach (ReporterCommsData report in reportData)
            {                
                ...
                if (String.IsNullOrEmpty(report.Email))
                {
                    ...
                }
                else
                {
                    try
                    {
                        MailMessage email = null;
                        ...
                            email = ConstructEmail(<param>, out <param>, <param>);
                        ...
                        bulkEmails.Add(report.BenefitID, email); //add each email to the bulk email dictionary
                        ...
                    }
                    catch (Exception ex)
                    {   
                        ...
                    }
                }
            } //end foreach

            //do the bulk email send and get the outcomes
            try
            {
                ...
                emailSuccessByBenefit.AddRange(Utilities.Mail.SendBulkEmail(bulkEmails, credentials));
            }
            catch (Exception ex)
            {
                ...
            }
        }
        ...
    }
    ...
}

在公用事业项目中

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Threading;

namespace <company>.Utilities
{
    ...
    public class Mail
    {
        private static List<EmailOutcome> m_MultithreadedEmailSendResults = new List<EmailOutcome>();
        private static int m_CompletedWorkers = 0;
        ...
        /// <summary>
        /// Sends a large number of emails asynchronously and then reports success of the individual items collectively
        /// </summary>
        /// <param name="emails">A dictionary of completed MailMessage objects to send out, keyed on an ID</param>
        /// <param name="credentials">Network credentials which may be required to send the email</param>
        /// <returns>List of EmailOutcome objects signifying the success or failure of sending each individual email</returns>
        public static List<EmailOutcome> SendBulkEmail(Dictionary<int, MailMessage> emails, System.Net.NetworkCredential credentials = null)
        {
            const int NUMBER_OF_THREADS_PER_PROCESSOR = 1;
            m_CompletedWorkers = 0;

            List<EmailOutcome> results = new List<EmailOutcome>();
            List<Dictionary<int, MailMessage>> splitEmailList = new List<Dictionary<int, MailMessage>>();
            ...
            List<BackgroundWorker> workerThreads = new List<BackgroundWorker>();

            foreach (Dictionary<int, MailMessage> splitEmails in splitEmailList)
            {
                // Initialise the parameter array
                Object[] parameterArray = new Object[2];
                parameterArray[0] = splitEmails;
                parameterArray[1] = credentials;

                // Runs on function startup
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += new DoWorkEventHandler(BulkEmailWorker);
                worker.WorkerReportsProgress = false;
                worker.WorkerSupportsCancellation = true;
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BulkEmailCompleted);

                //Add worker to collection
                workerThreads.Add(worker);

                //Calling the BulkEmailWorker asynchronously
                worker.RunWorkerAsync(parameterArray);
            }

            //Hold until all background workers complete
            while (workerThreads.Count > m_CompletedWorkers)
            {
                Thread.Sleep(500); //Wait a half second
            }

            //Dispose of BackgroundWorkers
            foreach (BackgroundWorker worker in workerThreads)
            {
                worker.Dispose();
            }

            //Get results
            results.AddRange(m_MultithreadedEmailSendResults);

            //Clear the static variable
            m_MultithreadedEmailSendResults.Clear();
            m_MultithreadedEmailSendResults = new List<EmailOutcome>();

            return results;
        }
        ...

        /// <summary>
        /// Event handler for the RunWorkerCompleted event. Adds the EmailOutcome results to the static 
        /// </summary>
        private static void BulkEmailCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            //Add the EmailOutcome objects to the static list of results if completed
            if (worker != null)
            {
                Thread.Sleep(200);
                worker.RunWorkerAsync();
            }
            else
            {
                m_MultithreadedEmailSendResults.AddRange(e.Result as List<EmailOutcome>);
                m_CompletedWorkers++;
            }
        }
        ...
    }
    ...
}

1 个答案:

答案 0 :(得分:0)

asp.net的工作方式是为每个请求创建一个Page对象。控制也是如此。您发送的电子邮件通知只能到达此对象。

您创建的线程将花费时间执行,您的响应将不会等待电子邮件发送完成。这意味着您无法使用相同的请求发送状态。

但是,如果您从页面发出另一个请求,ajax或其他内容,以获取更新状态,将创建一个新的页面和相应的控制对象。您必须从静态对象获取状态,并使用它来向用户显示状态。

您可能会发现UpdatePanel控件可以实现ajax。