5

I have a method that returns XML elements, but that method takes some time to finish and return a value.

What I have now is

foreach (var t in s)
{
    r.add(method(test));
}

but this only runs the next statement after previous one finishes. How can I make it run simultaneously?

dtb
  • 213,145
  • 36
  • 401
  • 431
ikel
  • 1,790
  • 6
  • 31
  • 61

4 Answers4

13

You should be able to use tasks for this:

//first start a task for each element in s, and add the tasks to the tasks collection
var tasks = new List<Task>();
foreach( var t in s)
{
    tasks.Add(Task.Factory.StartNew(method(t)));
}

//then wait for all tasks to complete asyncronously
Task.WaitAll(tasks);

//then add the result of all the tasks to r in a treadsafe fashion
foreach( var task in tasks)
{
  r.Add(task.Result);
}

EDIT There are some problems with the code above. See the code below for a working version. Here I have also rewritten the loops to use LINQ for readability issues (and in the case of the first loop, to avoid the closure on t inside the lambda expression causing problems).

var tasks = s.Select(t => Task<int>.Factory.StartNew(() => method(t))).ToArray();

//then wait for all tasks to complete asyncronously
Task.WaitAll(tasks);

//then add the result of all the tasks to r in a treadsafe fashion
r = tasks.Select(task => task.Result).ToList();
Øyvind Bråthen
  • 59,338
  • 27
  • 124
  • 151
  • Error 1 The best overloaded method match for 'System.Threading.Tasks.TaskFactory.StartNew(System.Action)' has some invalid arguments i keep getting error on tasks.Add(Task.Factory.StartNew(method(t))); – ikel Jan 11 '12 at 07:48
  • Some problems with my code there I see. Give me two minutes to edit it for you. – Øyvind Bråthen Jan 11 '12 at 08:19
  • ikel - There, see my edit. Code got cleaned up a bit as well :) – Øyvind Bråthen Jan 11 '12 at 08:24
  • thanks for the help, but i still get errors on the last line regarding task=>task.Result, is task defined in your code?? – ikel Jan 11 '12 at 08:38
  • You are sure you are using `Task` and not just `Task` on the first line? This compiles and works fine on my machine. – Øyvind Bråthen Jan 11 '12 at 09:07
7

You can use Parallel.ForEach which will utilize multiple threads to do the execution in parallel. You have to make sure that all code called is thread safe and can be executed in parallel.

Parallel.ForEach(s, t => r.add(method(t));
Anders Abel
  • 67,989
  • 17
  • 150
  • 217
  • this is something new, never used this before, i will try and get back here, thanks a lot – ikel Jan 11 '12 at 06:44
  • 6
    This is almost ok, but the problem here is that the add method on r (I expect r to be a List or similar) is not thread safe, and adding to the List from many different threads **will** cause a problem. – Øyvind Bråthen Jan 11 '12 at 06:45
1

Modification to the correct answer for this question change

tasks.Add(Task.Factory.StartNew(method(t);));

to

//solution will be the following code
tasks.Add(Task.Factory.StartNew(() => { method(t);}));
Stanislav
  • 27,441
  • 9
  • 87
  • 82
Rama Krshna Ila
  • 486
  • 3
  • 11
1

From what I'm seeing you are updating a shared collection inside the loop. This means that if you execute the loop in parallel a data race will occur because multiple threads will try to update a non-synchronized collection (assuming r is a List or something like this) at the same time, causing an inconsistent state.

To execute correctly in parallel, you will need to wrap that section of code inside a lock statement:

object locker = new object();
Parallel.Foreach (s, 
   t => 
   {  
      lock(locker) r.add(method(t));
   });

However, this will make the execution actually serial, because each thread needs to acquire the lock and two threads cannot do so at the same time.

The better solution would be to have a local list for each thread, add the partial results to that list and then merge the results when all threads have finished. Probably @Øyvind Knobloch-Bråthen's second solution is the best one, assuming method(t) is the real CPU-hog in this case.

Tudor
  • 61,523
  • 12
  • 102
  • 142