SAF(NET) = STEPHEN A. FUQUA operating on the Web since 1995

Stephen is a web developer, Bahá'í, and interfaith activist in St. Paul, Minnesota. He likes to write about religion, social justice, sustainability, science, programming, &c.

December 7, 2007

Threading and Waiting with Delegates

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.run TheProcess(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 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();

Comments

Post a comment

Remember personal info?




deprecated

On safnet.com

Other sites managed or developed by S.A.F.

S.A.F. elsewhere on the web

  • LinkedIn
    LinkedIn can actually be useful when looking for prospective hires and business or organizational partners
  • GoodReads
    A fun and relatively-unknown social networking site geared towards one's book list
  • Live Journal
    Mirror of the blog at safnet.com, so that a few LJ friends can more easily read and comment there