Thread Pool Optimization
I am working on a Multi-Threaded Async form application which sends mass emails. I nearly finished the application but have concerns about the performance. If you enlighten me with better best practices, it will be perfect and I will really learn basics as I am a junior developer.
Here is the question : First I have Start button for the application which triggers a Windows.Forms.Timer component.
private void btnStart_MouseClick(object sender, MouseEventArgs e)
{
timerNewCampaignChecker.Tick += new EventHandler(timerNewCampaignChecker_Tick);
timerNewCampaignChecker.Enabled = true;
timerNewCampaignChecker.Start();
}
Application needs to check database every 3 seconds for new campaigns(1st method), customize the the campaign details(2nd one) inline to user input and send the campaign to receivers via email(3rd one). In order to check database every 3 seconds for new campaigns I have the above timer(interval is 3 seconds) that starts the flow by checking new campaigns:
MethodInvoker invoker;
private void timerNewCampaignChecker_Tick(object sender, EventArgs e)
{
invoker = new MethodInvoker(CheckNewCampaigns);
invoker.BeginInvoke(null, null);
}
So, the timer triggers CheckNewCampaigns method in every 3 seconds and let the process begin from its 1st step. Is it right to start the flow and ThreadPool structure from the timer itself??? Now CheckNewCampaigns checks the DB, creates a List object, and for every campaign, it sends the details of the object to another method by invoking 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);
}
}
Do I have to code endInvoke in the first method, or should I just fire and forget??. Because the most important thing is that application has a flow, parameters are passed between methods, 2nd method should know when 1st one is finished so that it can take the campaignID and customize it. But while doing these, a new flow should start and check new campaigns which is 1st method. And also emails should be sent at the same moment. The New method(2nd one) customizes the campaign and invokes another method(3rd one) by using ThreadPool also. Here it is :
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;
}
I guess there is a main problem in this second step.There is a开发者_StackOverflow中文版 database operation for 5-6 times nearly. This can a be aproblem for the thread maybe. It seems working fine, but my way can be wrong. There can be tens of campaign one day. Is it a problem, or is my way fine?(DB operations are not so harsh and complicated by the way).
After preparing email, it is ready to be sent which is invoked above. Third method is a simple SMTP method that sends email and logs it.
This last method also does not have EndInvoke function but this is the last method of the application as mail just sent.
Does my way of dealing with timer and ThreadPool seem logical to you?? How may I test the performance in best way and optimize the working of threadpool?? What can you suggest and teach for any of the above?
Thanks a lot.
First of all you should be aware that .NET has a variety of timer options to choose from depending on your needs. System.Windows.Forms.Timer is used when you want the callback to execute on the UI thread (because you need to refresh the UI in any way). System.Timers.Timer and System.Threading.Timer both execute the callback on a background thread. You can read more about these in this MSDN article.
You should always invoke EndInvoke when using BeginInvoke to execute a delegate asynchronously, as explained in the documentation. To execute methods in a fire-and-forget manner the recommended approach is to use ThreadPool.QueueUserWorkItem (prior to .NET 4) or Task.TaskFactory.StartNew (.NET 4). The latter has the advantage that there is no 1:1 correlation between threads and tasks - the scheduler will pick "an appropriate" number of threads to use and schedule the tasks to execute on those threads for you.
You are unlikely to achieve the best performance simply by spawning many tasks. I'd create a custom queue class based on the new ConcurrentQueue class (added in .NET 4) and put outgoing emails into this, then spawn a few tasks to empty the queue by delivering the mails to the SMTP server. ConcurrentQueue is thread safe and you can therefore safely add and remove items from multiple threads using it.
精彩评论