"Threading enables your C# program to perform concurrent processing so you can do more than one operation at a time. For example, you can use threading to monitor input from the user, perform background tasks, and handle simultaneous streams of input."
Reference: http://msdn.microsoft.com/en-us/library/ms173178(v=vs.80).aspx
In other words, it's a beautiful thing. It can be one of your best allies in terms of performance if used correctly (when needed) or one of the biggest headaches to debug. Personally, I love threading! Buuuuut my fellow colleagues would say I have more faith in it than I should.
Currently I am working on the new backend core architecture for our newest product. It's an MVC application that will be able to run on any device (mobile, tablet, desktop, phablet, etc...), offer multilanguage support, and will have all the bells and whistles that new code can give your application (performance-wise) if you had the chance to write it all over again.
That being said, as part of the core architecture I have a set of tools, factories and utilities available for our other developer's to use. Ideally, I'd like to keep this as an N-Tier application and have our other developers use the core library for all the needs of any tier or app we have leaving the core to the R&D Team. These utilities and factories include functionality for Maps, Globalization, Diagnostics, Type Extensions, IO, Messaging, Runtime Tasks, DA, Data Manipulation, Caching, Attributes, Logging, Models, etc...
One of the coolest utilities that came out from all this new architecture work was a wrapper I created over the System.Threading.ThreadPool
. This would give our developers a central place to safely run and implement multiple tasks at once, relying on Microsoft's QueueUserWorkItem
and C# Generics. You can give the ThreaderPool
any declared in/out type or just an out type, and assign it any Action you want.
Example, let's say you have a quoting system and you have three different requests that you need to send off to your engine to get your responses. Simply assign the method to call, and add your QuoteRequest
to the pool, and run the method. This will return the results in the same order as entered.
using (ThreaderPool<QuoteRequest, QuoteResponse>
pool = new ThreaderPool<QuoteRequest, QuoteResponse>(q.GetQuote))
{
pool.Add(request1);
pool.Add(request2);
pool.Add(request3);
pool.Run();
List<QuoteResponse> responses = pool.Results;
}
Or, let's say you have different methods to call with either the same or different requests. A method for different types of responses.
using (ThreaderPool<QuoteRequest,
QuoteResponse> pool = new ThreaderPool<QuoteRequest, QuoteResponse>())
{
pool.Add(q.GetQuote, request1);
pool.Add(q.GetQuoteLTL, request2);
pool.Add(q.GetQuote, request3);
pool.Run();
List<QuoteResponse> responses = pool.Results;
}
It's literally that simple. This baby comes with exception handling too.
catch(ThreaderPoolRunException<QuoteRequest> ex)
{
Console.WriteLine(ex.Message);
foreach (varitem in ex.Exceptions)
{
QuoteRequest qr = item.Key;
Exception e = item.Value;
Console.WriteLine(e.Message);
Console.WriteLine("For request with number {0}\r\n\r\n", qr.Number);
}
}
List<QuoteResponse> responses = pool.Results;
As part of a Dev Samples page, I was showing everyone an example of each custom component we were writing too and I used this pool as an example of how to use it in MVC development. Now before you guys say anything, I know there's the Async controller to do this kind of work. But this was created before I knew about that guy. So another example.
public ActionResultSamples()
{
DevSamplesModel model = newDevSamplesModel();
ActionResult result = View(model);
using (ThreaderPool<ComponentBase> pool = newThreaderPool<ComponentBase>())
{
pool.Add(GetButtonModel);
pool.Add(GetCalendarModel);
pool.Add(GetDropDownListModel);
pool.Add(GetGridModel);
pool.Add(GetHtmlEditorModel);
pool.Add(GetMemoModel);
pool.Add(GetMenuModel);
pool.Add(GetNavBarModel);
pool.Add(GetRadioButtonListModel);
pool.Add(GetRoundPanelModel);
pool.Add(GetTrackBarModel);
pool.Add(GetTreeViewModel);
pool.Add(GetWindowModel);
pool.Add(GetCheckBoxModel);
pool.Add(GetCheckBoxListModel);
pool.Add(GetLabelModel);
pool.Add(GetImageModel);
pool.Add(GetProgressBarModel);
pool.Add(GetListBoxModel);
ThreadOperations operations = new ThreadOperations();
operations.Assign(
onError: (e) =>
{
RoundPanelModel panelModel = new RoundPanelModel("pnlError");
panelModel.HeaderText = "Error";
panelModel.Content = "Oops! ";
foreach (Exceptionexception in e.Exceptions)
{
panelModel.Content += exception.Message + "<br />";
}
result = PartialView(UIView.Components.RoundPanel, panelModel);
}
);
pool.Run(operations);
List<ComponentBase> responses = pool.Results;
if (responses != null)
{
model.ButtonModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "ButtonModel") asButtonModel;
model.CalendarModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "CalendarModel") as CalendarModel;
model.DropDownListModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "DropDownListModel") as DropDownListModel;
model.GridModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "GridModel") asGridModel;
model.HtmlEditorModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "HtmlEditorModel") as HtmlEditorModel;
model.MemoModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "MemoModel") asMemoModel;
model.MenuModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "MenuModel") asMenuModel;
model.NavBarModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "NavBarModel") asNavBarModel;
model.RadioButtonListModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "RadioButtonListModel") as RadioButtonListModel;
model.RoundPanelModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "RoundPanelModel") as RoundPanelModel;
model.TrackBarModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "TrackBarModel") as TrackBarModel;
model.TreeViewModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "TreeViewModel") as TreeViewModel;
model.WindowModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "WindowModel") asWindowModel;
model.CheckBoxModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "CheckBoxModel") as CheckBoxModel;
model.CheckBoxListModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "CheckBoxListModel") as CheckBoxListModel;
model.LabelModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "LabelModel") asLabelModel;
model.ImageModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "ImageModel") asImageModel;
model.ProgressBarModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "ProgressBarModel") as ProgressBarModel;
model.ListBoxModel = responses.FirstOrDefault
(r => r != null && r.GetType().Name == "ListBoxModel") asListBoxModel;
}
}
return result;
}
Below is a view of the ThreaderPool
class to give you an idea of how I accomplished this. So far, after much load testing, it hasn't been the point of any failure and has greatly improved the speed on some back end architecture work.
To view the rest of the files, click the Download button below.
Note: This was originally from my own code library Imfaqncodn.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Imfaqncodn.Runtime.Threading
{
public sealed class ThreaderPool<In, Out> : IDisposable
{
internal static List<ThreadPoolCounter> ThreadPoolCounters { get; set; }
internal static object action_locker = newobject();
private List<ThreaderPoolAction<In, Out>> Actions { get; set; }
private ThreaderPoolAction<In, Out>.ThreaderPoolActionEventHandler E { get; set; }
public List<Out> Results { get; set; }
public ThreaderPool() { }
public ThreaderPool(ThreaderPoolAction<In, Out>.ThreaderPoolActionEventHandler e)
: base()
{
this.E = e;
}
public void Add(In i)
{
Add(this.E, i);
}
public void Add(ThreaderPoolAction<In, Out>.ThreaderPoolActionEventHandler e, In i)
{
if (Actions == null)
{
Actions = new List<ThreaderPoolAction<In, Out>>();
}
ThreaderPoolAction<In, Out> action = new ThreaderPoolAction<In, Out>(i);
action.OnThreaderPool += e;
Actions.Add(action);
}
public void Run(ThreadOperations<In> operations = null)
{
if (Actions != null)
{
Guid guid = Guid.NewGuid();
using (ThreadPoolCountercounter = new ThreadPoolCounter()
{
Identifier = guid
,
NumberOfThreadsNotYetCompleted = Actions.Count()
,
Event = new ManualResetEvent(false)
})
{
if(ThreadPoolCounters == null)
{
ThreadPoolCounters = new List<ThreadPoolCounter>();
}
ThreadPoolCounters.Add(counter);
try
{
int i = 0;
foreach (var action in Actions)
{
action.Identifier = counter.Identifier;
i++;
ThreadPool.QueueUserWorkItem(action.ThreadPoolCallback, action.Request);
}
counter.Event.WaitOne();
this.Results = new List<Out>();
foreach (var action in Actions)
{
this.Results.Add(action.Response);
}
if (Actions.Any(a => a.exception != null))
{
ThreaderPoolRunException<In>
exceptions = new ThreaderPoolRunException<In>();
foreach (var action in Actions.Where(a => a.exception != null))
{
exceptions.Exceptions.Add(action.Request, action.exception);
}
operations.onError(exceptions);
}
}
finally
{
ThreadPoolCounters.Remove(counter);
}
}
}
}
#regionIDisposable Members
private bool disposed = false;
void IDisposable.Dispose()
{
Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected voidDispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.Actions = null;
this.E = null;
this.Results = null;
}
disposed = true;
}
}
#endregion
}
}
CodeProject
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.