tags:

views:

289

answers:

4

Hi i have this code:

var list = new List<int>();
for(int i=0;i<10;i++) list.Add(i); 
for(int i=0;i<10;i++)
{
     ThreadPool.QueueUserWorkItem(
         new WaitCallback(x => {
             Console.WriteLine(x);  
         }), list[i]);
} 

And i want to know when all threadpools threads finished their work. How i can to do that?

+5  A: 

You'll need to track this yourself.

One option for this is to use a counter and a reset event:

int toProcess = 10;
using(ManualResetEvent resetEvent = new ManualResetEvent(false))
{
    var list = new List<int>();
    for(int i=0;i<10;i++) list.Add(i); 
    for(int i=0;i<10;i++)
    {
        ThreadPool.QueueUserWorkItem(
           new WaitCallback(x => {
              Console.WriteLine(x);  
              // Safely decrement the counter
              if (Interlocked.Decrement(ref toProcess)==0)
                 resetEvent.Set();

           }),list[i]);
    } 

    resetEvent.WaitOne();
}
// When the code reaches here, the 10 threads will be done
Console.WriteLine("Done");
Reed Copsey
lol. I think 'done' is better name that 'mre' though
Remus Rusanu
@Remus: that better?
Reed Copsey
Of course, it makes all the difference ;)
Remus Rusanu
Don't forget to dispose the reset event - unmanaged resource.
Aaronaught
@Aaronaught: Added that in for you ;)
Reed Copsey
Excellent, +1 now!
Aaronaught
Why you are using Interlocked.Decrement(ref toProcess) insted of --toProcess?
Neir0
@Neir0: Since many threads will be potentially doing it at the same time, you need to protect that decrement. There are two options - use Interlocked, or use some form of locking. If you just use --toProcess, and two threads try to do it at the exact same time, you won't ever reach zero.
Reed Copsey
+2  A: 

I am not sure if ThreadPool exposes such functionality but you can use wait handles and by the way iterating twice seems unnecessary:

var events = new ManualResetEvent[10];
var list = new List<int>();
for (int i = 0; i < 10; i++)
{
    list.Add(i);
    events[i] = new ManualResetEvent(false);
    int j = i;
    ThreadPool.QueueUserWorkItem(x => {
        Console.WriteLine(x);
        events[j].Set();
    }, list[i]);
}
WaitHandle.WaitAll(events);
Darin Dimitrov
+1 - same solution as I posted; only you were quicker and presented slightly more elegant code.
Fredrik Mörk
OK, you were faster than I, too.
Timores
Just a warning with this... WaitHandle.WaitAll will fail if you have more than 64 items in an STA thread...
Reed Copsey
@Reed, good point.
Darin Dimitrov
Why create 10 events when you only need 1?
ChaosPandion
@Darin http://stackoverflow.com/questions/2521372/paging-through-recordsjson-data-using-jquery
Pandiya Chendur
@Pandiya Chendur, please stop posting comments about your questions on threads that are completely irrelevant to the topic discussed here. I saw your question and the answers you were given (especially the one about using jqGrid) which I think is a good answer and that's the reason why I didn't respond to you. Also don't post duplicate questions, this will not give you more/better answers.
Darin Dimitrov
@Darin sorry man...
Pandiya Chendur
+1  A: 

The thread pool does not tell you when the thread has finished executing, so the work item must do it itself. I changed the code like this:

    var list = new List<int>();
    ManualResetEvent[] handles = new ManualResetEvent[10];
    for (int i = 0; i < 10; i++) {
        list.Add(i);
        handles[i] = new ManualResetEvent(false);
    }
    for (int i = 0; i < 10; i++) {
        ThreadPool.QueueUserWorkItem(
         new WaitCallback(x =>
         {
             Console.WriteLine(x);
             handles[(int) x].Set();
         }), list[i]);
    }

    WaitHandle.WaitAll(handles);
Timores
A: 

This is how I would do it.

class Program
{
    static void Main(string[] args)
    {
        var items = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        using (var countdown = new Countdown(items.Length))
        {
            foreach (var item in items)
            {
                ThreadPool.QueueUserWorkItem(o =>
                {
                    Thread.SpinWait(100000000);
                    Console.WriteLine("Thread Done!");
                    countdown.Signal();
                });
            }
            countdown.Wait();
        }
        Console.WriteLine("Job Done!");
        Console.ReadKey();
    }

    public class Countdown : IDisposable
    {
        private readonly ManualResetEvent done;
        private readonly int total;
        private volatile int current;

        public Countdown(int total)
        {
            this.total = total;
            current = total;
            done = new ManualResetEvent(false);
        }

        public void Signal()
        {
            lock (done)
            {
                if (current > 0 && --current == 0)
                    done.Set();
            }
        }

        public void Wait()
        {
            done.WaitOne();
        }

        public void Dispose()
        {
            done.Dispose();
        }
    }
} 
ChaosPandion
This would be more performant using Interlocked.Decrement instead of a lock. See my answer for the relevant code.
Reed Copsey