Recently I was in need of increasing the performance of a lengthy task - Generating a PDF out of a set of data; which consists of "screenshots" of a series of webpages, set in certain order.
The initial design of the "generating" tool consisted of giving the generating method a list of URLs, then query the webpages in the given order (each URL is a page in the document), and then compile the document. The library we utilized to generate the pdf document requires that each page's generation be done in an STA environment, therefore, each page was generated on its own individual thread; and these were done sequentially; with a wait at the end of each page.
To increase the performance, we decided to make use of the existing threading and use a simple logic to enable multithreading so that all pages are generated simultaneously.
List<Thread> generatingThreads = new List<Thread>();
foreach (string parameter in listOfParameters)
{
ThreadParameter threadParameters = new ThreadParameter { parameter };
ParameterizedThreadStart pts = new ParameterizedThreadStart(PDFGenerator.ConvertPage);
Thread t = new Thread(pts);
generatingThreads.Add(t);
t.SetApartmentState(ApartmentState.STA);
t.Start(parameters);
}
bool conversionCompleted = false;
while (!conversionCompleted) //Wait until all pages are converted
{
conversionCompleted = true;
foreach (var thread in generatingThreads)
{
if (!thread.IsAlive)
{
continue;
}
else
{
conversionCompleted = false;
}
}
}
Notice how each conversion's thread is added to a list, and after all threads have been started; a loop prevents the code from continuing and completing the document creation until all conversions are finished. This logic is crude and probably there are better methods of accomplishing the same result; but for our simple scenario, it works remarkably well.
However, we still had a big problem - the process still took over 20 seconds, and the user would be wondering whether the process has not gotten stuck! Therefore, we decided to improve the process even further by making the process asynchronous. That is: Perform the generation task on a different thread and return control to the web application immediately. The advantage is obvious - the user is never let wondering whether the application has not gone "bad" and can continue working while the pdf (or any other lengthy task) goes on. The only disadvantage is that the results of the lengthy task (the pdf in our scenario) is not immediately available. In our case, this seemed a reasonable tradeoff.
The .Net framework makes the task of making ANY method asynchronous very easy. All you need to do is create a delegate method declaration and call the delegate method asynchronously. Let me walk you through the steps:
Say the method you wish to convert is
public void ITakeALongTime(List<string> parameters1, int parameter2) {...}
which you normally call like this:
myClass.ITakeALongTime(new List<string> { "param1", "param2", "param3"}, 42);
Then, what you need to do is declare the delegate like so:
public delegate void DelegateITakeALongTime(List<string> parameters1, int parameter2);
and call the method asynchronously like so:
public void ITakeALongTimeAsynchronous(List<string> parameters1, int parameter2)
{
// create the delegate
DelegateITakeALongTime theDelegate = new DelegateITakeALongTime(ITakeALongTime);
// call the BeginInvoke
IAsyncResult tag = theDelegate.BeginInvoke(parameters1, parameter2, null, null);
}
And that's it! When BeginInvoke is called, ITakeALongTime, thru the delegate, will be called asynchronously. Something worth noticing is that BeginInvoke, as we are using it, takes a list of parameters where the first parameters are the parameters that will be passed to the delegate (and then to the lengthy process that will be executed asynchronously), and the last parameters "null, null" are not part of the of our method but actually part of the begin invoke; which for our simple scenario, are null and null.
Note: IAsyncResult resides in System.Windows.Forms
There are more things that can be done; like subscribing to the end event (for instance, to notify the user the task has finished), but in our scenario there are not necessary; hence I've not delved on them. See this MSDN post for more information, patterns, and examples.
Hoping this helps you,
Stay coding my friends!
Rod