views:

360

answers:

5

I've got a loop like this

ICollection<Data> GetData()
{
  ICollection<Data> result = new List<Data>()
  foreach (var item in aCollection)
    item.AddData(result);
  return result;
}

and I now need this to be executed in parallel instead iteratively. My first attempt was to do soemthing like this

ICollection<Data> GetData()
{
  ICollection<Data> result = new SynchronizedCollection<Data>()
  foreach (var item in aCollection)
    new Thread(delegate() { item.AddData(result); }).Start();
  return result;
}

but I need a way to wait for all the data to be added before I return the result.

What would be the simplest way to do this?

Edit: That AddData will call across a network. In the collection usually are up to a few dozen entries.

+1  A: 

In C#4.0, you can use something like this:

Parallel.ForEach<Data>(aCollection, delegate(Data d)
{
  d.AddData(result);
});
Razzie
There's an approach for 3.5 (if you can't afford 4.0 yet) http://blog.robvolk.com/2009/06/parallel-foreach-loop-in-c-35.html
Marcel J.
But as I understand it, C# 4.0 isn't released yet, is it? I have to get this to work with VS 2008.
sbi
Marcel, why isnb't this an answer? It's probably the best one so far.
flq
It's not released yet, no, but you didn't specify which framework :-) As Marcel states, you can implement your own version. I also found this one: http://www.codeproject.com/KB/dotnet/PoorMansParallelForEach.aspx but I can't of course say whether that works well or not (the code looks alright).
Razzie
A: 

Store references to the thread objects you created and call join on every one of them:

ICollection<Data> GetData()
{
  ICollection<Data> result = new SynchronizedCollection<Data>()
  List<Thread> threads=new List<Thread>();

  foreach (var item in aCollection)
  {
    Thread thread=new Thread(delegate() { item.AddData(result); });
    thread.Start();
    threads.Add(thread);
  }

  foreach(Thread thread in threads)
    thread.Join();

  return result;
}
Blindy
You should use ThreadPool instead of starting threads on your own. ThreadPool was designed for exactly that purpose and gives better performance than multiple threads created by yourself (imagine the overhead when aCollection contains more than 2.000 elements!), ThreadPool uses usually up to 4 threads (depending on the count of cores, Environment.ProcessorCount).
Oliver Hanappi
Perhaps, but I completed his attempt to do what he wanted.
Blindy
+2  A: 

Unless AddData is really, really slow the overhead of creating new threads will most likely be the bottleneck in your code.

If you do want to hand this off to other threads you should use the ThreadPool for this. As for waiting for the threads to complete, you can use wait handles to signal between the threads.

Brian Rasmussen
Brian, `AddData` is going to be calling into something across the network, so it is very likely that it's worth the trouble. However, I'm doing this in order to find out whether it's worth the trouble.
sbi
Fair enough, but I would still recommend using ThreadPool threads in order to amortize the cost of creating threads.
Brian Rasmussen
@Brian: Yep, that sounds good. Now: How do I do this?
sbi
@sbi: see Oliver's answer and if you need a good introduction please see http://www.albahari.com/threading/
Brian Rasmussen
@Brian: I've read through that website. It's just that often, when I do something using one facility of the framework, someone points out that, using some other facility, everything would be so much simpler. That's why I asked how to do this. (Did I say I'm a complete .Net/C# newbie?)
sbi
A: 

The simplest way is probably to wait for the parallels framework coming in 4.0.

Right ow, for starters, you need to be careful abut the thread creation there. If you have several hundreds or even thousands of items, that code will potentially kill your app, or at least your performance.

I would attempt to design the AddData method as an asynchronous call. The IAsyncResult has a WaitHandle. You can wait on those handles until all asyncresults state that the operation is done. Then you can carry on. Doing this stuff asynchronously should ensure that you use threads from the thread pool. That way you get pretty good performance and you reach a natural saturation of working threads if your list gets really large.

flq
I'm afraid I can't tell TPTB they'll have to tell the customers to wait for C# 4.0. There are usually up to a few dozen entries. How exactly would I do this asynchronous call thing?
sbi
+3  A: 

You can use Parallel Extensions which will be integrated in C# 4.0 and are available for C# 3.5 as separate library which you can download here: Microsoft Parallel Extensions to .NET Framework 3.5.

If you don't want to use Parallel Extensions, do not start a thread for every iteration, rather use ThreadPool instead, which enables better performance.

As I got to know in the comments of this answer, List<> is not thread-safe why you should use SynchronizedCollection<> instead.

Here is a code sample using thread pool.

        IEnumerable<object> providers = null;

        var waitHandles = new List<WaitHandle>();
        foreach (var provider in providers)
        {
            var resetEvent = new ManualResetEvent(false);
            waitHandles.Add(resetEvent);

            ThreadPool.QueueUserWorkItem(s =>
             {
                 // do whatever you want.
                 ((EventWaitHandle)s).Set();
             }, resetEvent);
        }

        WaitHandle.WaitAll(waitHandles.ToArray());
Oliver Hanappi
If I use ThreadPool, then how can I easily wait for all the tasks to be finished? (AS I understand it, `SynchronizedCollection` is thread-safe, BTW.)
sbi
You can provide event wait handles as state when queuing tasks to threadpool and wait for all event wait handles to be set.
Oliver Hanappi
@Oliver: `List<T>` isn't thread-safe. I'm guessing that's why the OP's multi-threaded example uses `SynchronizedCollection<T>`, which is thread-safe.
LukeH
Thanks for the information ;)
Oliver Hanappi
@Oliver: And, being a C# newbie, I'm asking how to do this in as simple a way as possible.
sbi
I have added a sample.
Oliver Hanappi
@Oliver: Thanks, this was exactly what I was looking for!
sbi