Scotty H
Scotty H

Reputation: 6694

C# Run Tasks in parallel and associate each with input

public class Input
{
    public bool ParameterOne { get; set; }
    public string ParameterTwo { get; set; }
}

I have a List<Input> that I want to process in parallel, keeping the input associated with the output. I loop through this list to build Tasks.

var results = new List<(Input, Output)>();
foreach (var input in inputs)
{
    Output output = await Process(input);
    results.Add((input, output));
}

But this is not operating in parallel. I would like it to.

var inputsAndTasks = new List<(Input Input, Task<Output> OutputTask)>();
foreach (var input in inputs)
{
    Task<Output> output = Process(input);
    inputsAndTasks.Add((input, output));
}
Output[] results = await Task.WhenAll(inputsAndTasks.Select(i => i.OutputTask));
// results are no longer associated with their inputs!

How can I run this in parallel but retain the input association with the output? I don't want to make the input an object on the output.

Upvotes: 2

Views: 396

Answers (2)

MPost
MPost

Reputation: 535

There is an alternative approach that avoids using tasks altogether by taking advantage of "AsParallel"

IList<Input> inputs = new List<Input>();
// populate inputs here

IDictionary<Input, Output> associations = new Dictionary<Input, Output>();
associations.AddRange(inputs.AsParallel()
                        .Select(async i => 
                            { Output o = await Process(i); 
                              return new KeyValuePair<Input, Output>(i, o);
                            }));

Upvotes: 3

juharr
juharr

Reputation: 32266

You can just await the WhenAll then do a loop to match up the input and output

var inputsAndTasks = new List<(Input Input, Task<Output> OutputTask)>();
foreach (var input in inputs)
{
    Task<Output> output = Process(input);
    inputsAndTasks.Add((input, output));
}

await Task.WhenAll(inputsAndTasks.Select(i => i.OutputTask));

var results = new List<(Input, Output)>();
foreach (var x in inputsAndTasks)
{
    Output output = await x.OutputTask;
    results.Add((x.Input, output));
}

Since you already know all the individual output tasks are complete doing the await in that second loop will return the result immediately.

Upvotes: 0

Related Questions