-2

I have a for loop and inside this for loop, it has a void method which would generally take time. I want to run this void method in an async way and want to call printFinalMessage() after doStuff(ele) is completed for all the ele in list.

for (int ele in list)
{
    doStuff(ele);
}

printFinalMessage()

Would appreciate any help.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75

3 Answers3

4

I would suggest just using a Parallel.ForEach loop. This will process elements concurrently, but not asynchronously. Meaning the loop will go faster, but printFinalMessage will only be printed when all items have actually completed processing:

Parallel.ForEach(list, ele => {
    doStuff(ele);
}
printFinalMessage();

This assumes that doStuff is an regular synchronous method. If it return a task you should probably use Task.WhenAll to create and task representing the completion of all of the tasks.

You might want to also run the entire loop in a background task, so the UI does not freeze while the loop is running. See how to run a task in the background.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • 1
    I like to specify the `ParallelOptions` when using the `Parallel.ForEach`, to prevent the default unlimited parallelism: `ParallelOptions options = new() { MaxDegreeOfParallelism = Environment.ProcessorCount };` – Theodor Zoulias Jan 12 '23 at 07:51
  • 1
    @TheodorZoulias I believe that the default behavior will not start more work than there are cores as long as the work is compute limited, so the usefulness of MaxDegreeOfParallelism should depend on circumstances. And there is also things like partitions, local init and other potentially useful features I think are to advanced for an answer like this., – JonasH Jan 12 '23 at 08:00
  • My opinion is that the default value `-1` is not a good default, and I have asked Microsoft to revision the advice offered currently in the docs, but my arguments [weren't convincing enough](https://github.com/dotnet/runtime/issues/72981 "ParallelOptions.MaxDegreeOfParallelism documentation, is the given advice correct?"). I have posted in that thread a couple of experiments, with results that some people might consider unexpected. – Theodor Zoulias Jan 12 '23 at 08:23
3

The keyword for your question is await all tasks this is reference from microsoft, add your task to a list and await all of them. Notice that the signature of DoStuff() is Task

var list = new List<int>() { 1, 2, 3};

var taskList = new List<Task>();

foreach(var ele in list)
{
    taskList.Add(DoStuff(ele));
    //or you can write like
    //var task = DoStuff(ele); 
    //taskList.Add(task);
}

await Task.WhenAll(taskList);

public Task DoStuff(int ele){ }
MichaelMao
  • 2,596
  • 2
  • 23
  • 54
0

What @JonasH said, but if you make doStuff async¹ then you could use Parallel.ForEachAsync²:

ParallelOptions parallelOptions = new () { MaxDegreeOfParallelism = N };

var list = new List<int>() { 1, 2, 3 };

await Parallel.ForEachAsync(
    source: list,
    parallelOptions,
    async (i, cancellationToken) =>
    {
        await Task.Delay(TimeSpan.FromSeconds(i), cancellationToken); 
        // or await doStuffAsync(i, cancellationToken);
    });

printFinalMessage(); // Called all work (3 pieces of work in this example) is done.

¹ - a real async; not a pretend async with - for example - Task.FromResult

² - available from .NET 6

tymtam
  • 31,798
  • 8
  • 86
  • 126