views:

68

answers:

2

Hello All

I have written a sample program below.

class Program
    {
        static int x = 2;

        static void Main(string[] args)
        {
            Console.WriteLine("Thread ID {0} and Main Called!", Thread.CurrentThread.ManagedThreadId);

            ThreadPool.QueueUserWorkItem(Count, args);
            ThreadPool.QueueUserWorkItem(Count2, args);

            Console.WriteLine("Thread ID {0} and Main Done!", Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }
        static void Count(object args)
        { 
            for (int i = 0; i < 10; i++)
            {
                x = x + 2;
                Console.WriteLine("Thread ID {0} AND Count 1: " + x, Thread.CurrentThread.ManagedThreadId);
            }
        }
        static void Count2(object args)
        {
            for (int i = 0; i < 10; i++)
            {
                x = x + 2;
                Console.WriteLine("Thread ID {0} AND Count 2: " + x, Thread.CurrentThread.ManagedThreadId);
            }
        }
    }

When calling the Count method using ThreadPool.QueueUserWorkItem, I noticed that Main is finished before the Count method completes and Count2 method gets tangled up with Count Method.

Is there anyway for Main (and Count2) to wait until the Count method has compleleted? I do not want to use a lock nor Thread.Sleep (since I dont know how long the Count operations will take). I have read somewhere async calls or WAIT is used for this time of situations.

Any ideas?

A: 

I think AutoResetEvent is what you are after

*EDIT: * Here is the modified code (untested). I used AutoResetEvent rather than Manual in this example

class Program
{
    static int x = 2;
    // Define an array with two AutoResetEvent WaitHandles.
    static WaitHandle[] waitHandles = new WaitHandle[] 
    {
        new AutoResetEvent(false),
        new AutoResetEvent(false)
    };



    static void Main(string[] args)
    {
        Console.WriteLine("Thread ID {0} and Main Called!", Thread.CurrentThread.ManagedThreadId);

        ThreadPool.QueueUserWorkItem(new WaitCallback(Count), waitHandles[0]);
        ThreadPool.QueueUserWorkItem(new WaitCallback(Count2), waitHandles[1]);
        WaitHandle.WaitAll(waitHandles);


        Console.WriteLine("Thread ID {0} and Main Done!", Thread.CurrentThread.ManagedThreadId);
        Console.ReadLine();
    }
    static void Count(object args)
    {
        AutoResetEvent are = (AutoResetEvent)args;

        for (int i = 0; i < 10; i++)
        {
            x = x + 2;
            Console.WriteLine("Thread ID {0} AND Count 1: " + x, Thread.CurrentThread.ManagedThreadId);
        }
        are.Set();

    }
    static void Count2(object args)
    {
        AutoResetEvent are = (AutoResetEvent)args;

        for (int i = 0; i < 10; i++)
        {
            x = x + 2;
            Console.WriteLine("Thread ID {0} AND Count 2: " + x, Thread.CurrentThread.ManagedThreadId);
        }
        are.Set();
    }
}
Dieter G
+2  A: 

I am going to present two patterns. Both of these patterns are highly scalable in that they can handle hundreds or even thousands of simultaneous work items. You must follow the patterns exactly. Any deviation might result in a race condition between the signaling of the event and the waiting of it. I have presented the patterns in the context of a loop to generalize it, but in your case you would replace the loop with two individual calls to ThreadPool.QueueUserWorkItem.

The first pattern requires the CountdownEvent class which is available in .NET 4.0 or as part of the Reactive Extensions download.

var finished = new CountdownEvent(1);
for (int i = 0; i < NUM_WORK_ITEMS; i++)
{
  finished.AddCount();
  ThreadPool.QueueUserWorkItem(
    (state) =>
    {
      try
      {
        // Your work item code goes here.
        // Call Count, Count2, or whatever.
      }
      finally
      {
        finished.Signal();
      }
    });
}
finished.Signal();
finished.WaitOne();

The following pattern will work with any version of the .NET Framework1. It is not as elegant though.

int count = 1;
var finished = new ManualResetEvent(false);
for (int i = 0; i < NUM_WORK_ITEMS; i++)
{
  Interlocked.Increment(ref count);
  ThreadPool.QueueUserWorkItem(
    (state) =>
    {
      try
      {
        // Your work item code goes here.
        // Call Count, Count2, or whatever.
      }
      finally
      {
        if (Interlocked.Decrement(ref count) == 0) finished.Set();
      }
    });
}
if (Interlocked.Decrement(ref count) == 0) finished.Set();
finished.WaitOne();

1Naturally you would have to replace the lambda expression with an anonymous method or even a real method if using much earlier versions.

Brian Gideon
upvote for pattern 2. works great. thanks.
Vince