0

I was looking for this and I couldn't figure how to do it.

I have a some threats (tasks) in an App.

    foreach (string targetMachine in targetMachines)
    {
        Task<int> task = Task.Run(() => Magic(targetMachine));
    }

I created a task for every object in an array. The task returns a value. I want to process the values and act based on them. As example, if task return 0 dont do anything, if returns 1 write a log, if returns 2 run a process.

How can I acoomplish this? If I process the return values inside the foreach:

foreach (string targetMachine in targetMachines)
    {
        Task<int> task = Task.Run(() => Magic(targetMachine));
        Task.Waitforexit()
        if (task.result == 2)
        {
        do something
        }
    }

I think, task are not going to be useful and the programa will wait each task to be completed to continue.

Ricardo Polo Jaramillo
  • 12,110
  • 13
  • 58
  • 83
  • 2
    If you're using C# <=4.0 you have a potentially really nasty bug where `targetMachine` might not be what you expect. C# <=4.0 declares (in IL) the `targetMachine` _outside_ the loop, but starting with C# 5.0 it declares it _inside_ the loop. The value of `targetMachine` might not be what you expect. Given 10 `targetMachines`, it's possible that you'll run the last 5 or so iterations with the last value of `targetmachines`. – kelloti Dec 02 '12 at 17:02
  • @kelloti I think I have this bug. Do you have any workarraound? – Ricardo Polo Jaramillo Dec 03 '12 at 06:33
  • 1
    This 'bug' has been Q+A many times. See http://stackoverflow.com/q/271440/ – H H Dec 03 '12 at 11:50

3 Answers3

2

Have a look at Task.ContinueWith()

http://msdn.microsoft.com/en-us/library/dd321405.aspx

When each task is complete it passes the result to the ContinueWith which can then either do nothing, log or call a method like you want.

Additionally you could run the foreach .AsParallel() and remove the tasks altogether.

http://msdn.microsoft.com/en-us/library/dd413602.aspx

Mark Vickery
  • 1,927
  • 3
  • 22
  • 34
2

It can be something like this:

foreach (string targetMachine in targetMachines)
{
    Task.Run(() =>
    {
        var result = Magic(targetMachine);
        if (result == 2)
        {
            DoSomething();
        }
    });
}

OR (using async/await)

foreach (string targetMachine in targetMachines)
{
    var result = await Task.Run(() => Magic(targetMachine));
    if (result == 2)
    {
        DoSomething();
    }
}

OR (using ContinueWith)

foreach (string targetMachine in targetMachines)
{
    Task<int>.Run(()=>Magic(targetMachine))
        .ContinueWith(t =>
        {
            if (t.Result == 2)
            {
                DoSomething();
            }
        });
}

If you want to wait to finish all of your tasks

Parallel.ForEach(targetMachines, targetMachine =>
{
    var result = Magic(targetMachine);
    if (result == 2)
    {
        DoSomething();
    }
});
L.B
  • 114,136
  • 19
  • 178
  • 224
  • No, on a second look all your samples seem to close over the loop variable. Only the Parallel.ForEach seems safe. – H H Dec 03 '12 at 23:48
  • @HenkHolterman async/await is only available in .Net 4.5, and it is safe to close over loop variable in 4.5. Others are also safe for 4.5. – L.B Dec 04 '12 at 06:39
0

I'll add a new variant in addition to the variants given in L.B.'s answer:

var tasks = targetMachines.Select(x => Magic(x));
Task.WaitAll(tasks); //fork-join

If you're using async, you can write:

var tasks = targetMachines.Select(x => Magic(x));
await Task.WhenAll(tasks); //fork-join

And instead of

var tasks = targetMachines.Select(x => Magic(x));

you can always write:

var tasks = targetMachines.Select(x =>
{
    var result = Magic(x);
    if (result == 2) DoSomething();
});

which liberates you from having to use ContinueWith.

usr
  • 168,620
  • 35
  • 240
  • 369