views:

543

answers:

4

I'm writing a program where I typically start five threads. The threads return in a non-determinate order. Each thread is calling a method which returns a List.

I'm doing this:

var masterList = List<string>();    
foreach (var threadParam in threadParams)
{
    var expression = threadParam ;
    ThreadStart sub = () => MyMethod(expressions);
    var thread = new Thread(sub)
    {
        Name = expression
    };

    listThreads.Add(thread);
    thread.Start();
}

var abort = true;
while (abort) //Wait until all threads finish
{
    var count = 0;
    foreach (var list in listThreads)
    {
        if (!list.IsAlive)
        {
            count++;
        }
    }

    if (count == listThreads.Count)
    {
        abort = false;
    }
}

So here is the problem:

Each thread when it terminates returns a list I would like to append the masterList declared earlier.

How would one go about this?

Also I KNOW there must be a better way than below to wait for all threads to finish

var abort = true;
while (abort) //Wait until all threads finish
{
    var count = 0;
    foreach (var list in listThreads)
    {
        if (!list.IsAlive)
        {
            count++;
        }
    }

    if (count == listThreads.Count)
    {
        abort = false;
    }
}
A: 

Check out the WaitHandle class and the WaitHandle.WaitAll (I've used the ManualResetEvent class in some of my code for the WaitHandle but it's just from an example. I don't know if there is something better for your situation). Fire off all five threads, give them a reference to your master list, lock this list when adding to it, then signal completion. Use WaitHandle.WaitAll to block until all five have signaled completion.

Max Schmeling
+2  A: 

The best way would be to make each thread it's own object. Don't have any intermingling with other objects, all you do is construct it (passing in the variables), add yourself as a listener and start it.

When it's done, it stores the values in a member variable and notifies your listener.

Your listener can retrieve the values at leisure.

The obvious shortcut, returning the values directly to the listener, works but you may find this version more flexible later (and really not much more code)

Bill K
+11  A: 

Use a WaitHandle


Here's an example:

using System;
using System.Threading;

class ThreadSleeper
{
    int seconds;
    AutoResetEvent napDone = new AutoResetEvent(false);

    private ThreadSleeper(int seconds)
    {
        this.seconds = seconds; 
    }

    public void Nap()
    {
        Console.WriteLine("Napping {0} seconds", seconds);
        Thread.Sleep(seconds * 1000);
        Console.WriteLine("{0} second nap finished", seconds);
        napDone.Set();
    }

    public static WaitHandle DoSleep(int seconds)
    {
        ThreadSleeper ts = new ThreadSleeper(seconds);
        Thread thread = new Thread(new ThreadStart(ts.Nap));
        thread.Start();
        return(ts.napDone);
    }
}

public class OperationsThreadsWaitingwithWaitHandle
{
    public static void Main()
    {
        WaitHandle[] waits = new WaitHandle[2];
        waits[0] = ThreadSleeper.DoSleep(8);
        waits[1] = ThreadSleeper.DoSleep(4);

        Console.WriteLine("Waiting for threads to finish");
        WaitHandle.WaitAll(waits);
        Console.WriteLine("Threads finished");
    }
}


Links to check out:

Andreas Grech
Same idea as mine... but a way better answer :(
Max Schmeling
A: 

You can of course use regular delegates as well and APM.

Note that the pattern you describe is normally described as a Future, a background job that promises to return something later on (a return on investment if you may).

Here's a short example, in the form of a console program.

Output:

sequential: 3224 ms
parallel: 2074 ms

Of course, since I'm not constructing explicit threads, I leave it to the threadpool system to figure out how many threads to run in parallel.

There's also provisions to be informed of when the background threads are completed, via a callback method, as well as WaitHandle support to explicitly checking and waiting with timeout, etc.

And the source:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;

namespace SO1215227
{
    public class Program
    {
        public static void Main()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var list1 = Sequence(1, 100);
            var list2 = Sequence(101, 200);
            var list3 = Sequence(201, 300);
            sw.Stop();
            Console.Out.WriteLine("sequential: " + sw.ElapsedMilliseconds + " ms");
            sw.Reset();

            Func<Int32, Int32, List<Int32>> listProducer = Sequence;
            sw.Start();
            var list1Background = listProducer.BeginInvoke(1, 100, null, null);
            var list2Background = listProducer.BeginInvoke(101, 200, null, null);
            var list3Background = listProducer.BeginInvoke(201, 300, null, null);

            list1 = listProducer.EndInvoke(list1Background);
            list2 = listProducer.EndInvoke(list2Background);
            list3 = listProducer.EndInvoke(list3Background);
            sw.Stop();
            Console.Out.WriteLine("parallel: " + sw.ElapsedMilliseconds + " ms");

            Console.Out.Write("Press enter to exit...");
            Console.In.ReadLine();
        }

        private static List<Int32> Sequence(Int32 from, Int32 to)
        {
            List<Int32> result = new List<Int32>();
            for (Int32 index = from; index <= to; index++)
            {
                result.Add(index);
                Thread.Sleep(10); // simulate I/O wait
            }
            return result;
        }
    }
}
Lasse V. Karlsen