我正在开发一个发送大量电子邮件的多线程异步表单应用程序。我几乎完成了该应用程序,但对性能有所担心。如果您以更好的最佳实践启发我,那将是完美的,并且我将在初级开发人员的基础上真正地学习基础知识。
这里是问题:首先,我有用于触发Windows.Forms.Timer组件的应用程序的“开始”按钮。
private void btnStart_MouseClick(object sender, MouseEventArgs e)
{
timerNewCampaignChecker.Tick += new EventHandler(timerNewCampaignChecker_Tick);
timerNewCampaignChecker.Enabled = true;
timerNewCampaignChecker.Start();
}
应用程序需要每3秒检查一次数据库以查找新的广告系列(第一种方法),内联定制广告系列详细信息(第二种)以供用户输入,然后通过电子邮件将广告系列发送给收件人(第三种方法)。为了每3秒检查一次数据库以查看新的广告系列,我有上面的计时器(间隔为3秒),它通过检查新的广告系列来启动流程:
MethodInvoker invoker;
private void timerNewCampaignChecker_Tick(object sender, EventArgs e)
{
invoker = new MethodInvoker(CheckNewCampaigns);
invoker.BeginInvoke(null, null);
}
因此,计时器每3秒触发一次CheckNewCampaigns方法,并从第一步开始。 从计时器本身启动流和ThreadPool结构是否正确?? 。
。因为最重要的是应用程序具有流程,所以方法之间会传递参数,因此第二个方法应该知道第一个方法何时完成,以便可以获取campaignID并对其进行自定义。但是,在进行这些操作时,应该首先启动新流程并检查新的广告系列。并且电子邮件应同时发送。New方法(第二个方法)定制广告系列,并使用ThreadPool调用另一个方法(第三个方法)。这是:delegate bool StepCaller(int campaignID); private void CheckNewCampaigns() { StringBuilder builder = new StringBuilder(); StepCaller stepCaller = new StepCaller(PrepareCampaignEmail); IEnumerable<Campaigns> CampaignsList = DatabaseManager.GetCampaignsList(DatabaseManager.GetNewCampaigns()); foreach (Campaigns Campaign in CampaignsList) { stepCaller.BeginInvoke(Campaign.CampaignID, new AsyncCallback(PrepareEmailCallback), null); } }
我必须在第一种方法中编写endInvoke的代码,还是我应该开除并忘记??
private bool PrepareCampaignEmail(int campaignID) { EmailCaller emailCaller = new EmailCaller(SendEmail); IEnumerable<Subscribers> SubscribersList = DatabaseManager.GetCampaignSubscribersList(DatabaseManager.GetCampaignSubscribers(campaignID)); CampaignCustomazition campaignCustomizer; List<CampaignCustomazition> campaignCustomizedList = new List<CampaignCustomazition>(); // foreach in içindeki beginInvoke ı dışarıya çıkardığımda kullanılacak foreach (Subscribers subscriber in SubscribersList) { campaignCustomizer = new CampaignCustomazition(campaignID); campaignCustomizer.CustomizeSource(campaignID, out campaignCustomizer.source, out campaignCustomizer.format); campaignCustomizer.CustomizeCampaignDetails(campaignID, out campaignCustomizer.subject, out campaignCustomizer.fromName, out campaignCustomizer.fromEmail, out campaignCustomizer.replyEmail); campaignCustomizer.CustomizeSubscriberDetails(campaignID, out campaignCustomizer.subscriberID, out campaignCustomizer.email, out campaignCustomizer.fullName); IAsyncResult result = emailCaller.BeginInvoke(campaignCustomizer, null, null); try { emailCaller.EndInvoke(result); } catch(Exception ex) { Trace.WriteLine(ex.Message); } } return false; }
我想这是第二步中的一个主要问题。数据库操作几乎是5-6次。这可能是线程的问题。似乎工作正常,但我的方法可能是错误的。一天可能会有数十个广告活动。这是一个问题,还是我的方法还行?(DB操作并不是那么苛刻和复杂)。
准备好电子邮件后,就可以发送了,上面已经提到了。第三种方法是发送电子邮件并将其记录的简单SMTP方法。
此最后一个方法也不具有EndInvoke功能,但这是应用程序的最后一个方法,因为刚刚发送了邮件。
我处理计时器和ThreadPool的方式对您来说是否合乎逻辑?如何以最佳方式测试性能并优化线程池的工作?您可以针对上述任何方面提出建议和教益吗?
非常感谢。
我正在开发一个发送大量电子邮件的多线程异步表单应用程序。我几乎完成了该应用程序,但对性能有所担心。如果您能更好地启发我...
首先,您应该意识到.NET具有多种计时器选项,可以根据需要进行选择。当您希望在UI线程上执行回调时使用System.Windows.Forms.Timer(因为您需要以任何方式刷新UI)。 System.Timers.Timer和System.Threading.Timer都在后台线程上执行回调。您可以在此MSDN article中详细了解这些内容。
如documentation中所述,使用BeginInvoke异步执行委托时,应始终调用EndInvoke。若要以一劳永逸的方式执行方法,建议的方法是使用ThreadPool.QueueUserWorkItem(.NET 4之前)或Task.TaskFactory.StartNew(.NET 4)。后者的优点是线程和任务之间没有1:1的关联-调度程序将选择“适当”数量的线程来使用,并为您调度在这些线程上执行的任务。