One of the casualties of not having a full computer science education is that I was barely exposed to threading. In my two Java classes we touched upon it, and wrote some basic examples in the second semester, but that could hardly be called extensive use. Thus, a number of years on, it has been a bit of a struggle for me to use threads in a practical manner. Further complicating the matter is that I’ve wanted to use anonymous methods (aka delegates) and generally want to do some work after all threads have finished executing.
Unfortunately most of the examples I’ve found deal with one or the other, but in a manner either too simplistic for what I wanted or at cross purposes. For instance, they look pretty static in terms of the number of threads spawned, or don’t deal at all with waiting until the threads have finished, or just don’t explain themselves very well. But at last I’ve found the final piece I needed, in Working with Delegates Made Easier with C# 2.0.
I tried creating an array of WaitHandle
objects, but somewhere got something
wrong. I’m sure they’re perfectly usable. But then the article above showed me
that all I really needed was the AutoResetEvent
. And in fact, all I needed was
one such object. Then to instantiate my thread I used a delegate
— but
did not declare a new delegate
, an early mistake of mine.
This anonymous method runs another function that takes an input paramete, and
eventually, when all threads are finished, releases the AutoResetEvent
.
Thankfully the code knows how to figure out how many threads will be spawned.
Finally, run the WaitOne
method on the instance of AutoResetEvent
in order
to pause execution of the rest of the program until all threads are finished.
/* Trying to process a bunch of files in a directory. */
FileInfo[] readyFiles = tmpDir.GetFiles();
/* These two variables are the key to signaling that all threads are done. */
AutoResetEvent are = new AutoResetEvent(false);
long numberLeft = readyFiles.Length;
/* Loop through all the files. Could've used a foreach loop, but for is slightly faster */
for (int i = 0; i < readyFiles.Length; i++)
{
FileInfo f = readyFiles[i];
/* Start a thread with an anonymous method */
ThreadPool.QueueUserWorkItem(delegate(Object obj)
{
try
{
/* A private function in the same class that does some action on the input file */
this.runTheProcess(f);
}
/* I found that the thread would loop infinitely if I tried to let exceptions go, therefore I just catch the exceptions and add them to a list. */
catch (Exception ex)
{
/* Important to lock this global list, otherwise can get a race condition. */
lock(this._excpList)
{
/* Actually, its a Dictionary<string, Exception> so that I can capture the file name and the exception together. */
this._excpList.Add(f.FullName, ex);
}
}
finally
{
/* Important to have this in the finally. Initially it was in the try. I
purposefully created an error condition. When I noticed that the threads never
stopped, I realized it was because this code had not been hit. */
if (Interlocked.Decrement(ref numberLeft) == 0)
are.Set();
}
});
}
/* Wait for all the threads to finish. */
are.WaitOne();
Posted with : Tech, Microsoft .NET Framework