9

is there a way for an Asynchronous foreach in C#? where id(s) will be processed asynchronously by the method, instead of using Parallel.ForEach

//This Gets all the ID(s)-IEnumerable <int>
var clientIds = new Clients().GetAllClientIds(); 

Parallel.ForEach(clientIds, ProcessId); //runs the method in parallel

static void ProcessId(int id)
{
// just process the id  
}

should be something a foreach but runs asynchronously

foreach(var id in clientIds)
{
   ProcessId(id) //runs the method with each Id asynchronously??
}

i'm trying to run the Program in Console, it should wait for all id(s) to complete processing before closing the Console.

alomegah
  • 117
  • 1
  • 1
  • 7
  • Not sure what you are asking here. I think you are asking for a multi-threaded foreach but you do not want to use the `Parallel.ForEach` (which is multi-threaded)? – Igor Oct 17 '16 at 16:50
  • Well, is your `ProcessId` function asyncrounous in the 2nd example? does it return a `Task`? You can't just "make a function asyncronous" you have to write it asyncronously or execute it on another thread (like `Parallel.ForEach` does) – Scott Chamberlain Oct 17 '16 at 16:50
  • 4
    @Igor `Parallel.ForEach` is not asynchronous, it synchronously processes tasks in parallel. Also it does not support async/await, so be careful of mixing the two. – Scott Chamberlain Oct 17 '16 at 16:51
  • Asychronous means it doesn't block the calling thread. Do you mean you want something that starts a bunch of threads to do stuff, but blocks on the calling thread until they all finish? – 15ee8f99-57ff-4f92-890c-b56153 Oct 17 '16 at 16:51
  • look into `Task.WhenAll` but the target method would need to return a `Task` – Nkosi Oct 17 '16 at 16:53
  • 1
    @ScottChamberlain - I think that is what the OP is asking though and many times people use mistakenly use `asynchronous` when they mean multi-threaded. I made that guess based on the last statement `run the Program in Console, it should wait for all id(s) to complete processing before closing the Console` To be fair I should have used the correct terminology myself in my comment. – Igor Oct 17 '16 at 16:54
  • 2
    @Igor I would agree but he says `Parallel.ForEach(clientIds, ProcessId); //runs the method in parallel` and then says `should be something a foreach but runs asynchronously`. He understands that `Parallel.ForEach` does it multi-threaded already, so I am not sure he means async as multithread. – Scott Chamberlain Oct 17 '16 at 16:55
  • Thanks guys, i just wanted to know if there's other way to run the method asynchronously with all the Id(s) at the same time (which is running the method in different Instance at the same time with each differrent Id) without using the Parallel.ForEach. – alomegah Oct 18 '16 at 06:18

3 Answers3

18

No, it is not really possible.

Instead in foreach loop add what you want to do as Task for Task collection and later use Task.WaitAll.

var tasks = new List<Task>();

foreach(var something in somethings)
 tasks.Add(DoJobAsync(something));

await Task.WhenAll(tasks);

Note that method DoJobAsync should return Task.

Update:

If your method does not return Task but something else (eg void) you have two options which are essentially the same:

1.Add Task.Run(action) to tasks collection instead

tasks.Add(Task.Run(() => DoJob(something)));

2.Wrap your sync method in method returning Task

 private Task DoJobAsync(Something something)
 {
     return Task.Run(() => DoJob(something));
 }

You can also use Task<TResult> generic if you want to receive some results from task execution.

Pawel Troka
  • 853
  • 2
  • 12
  • 33
  • Would this actually run it asynchronously? I am trying to replicated it on my code yet, it seems all my DoJobAsync instances run one after the other. – Anzurio Jan 24 '17 at 03:24
7

Your target method would have to return a Task

static Task ProcessId(int id)
{
    // just process the id  
}

Processing ids would be done like this

// This Gets all the ID(s)-IEnumerable <int>
var clientIds = new Clients().GetAllClientIds(); 
// This gets all the tasks to be executed
var tasks = clientIds.Select(id => ProcessId(id)).
// this will create a task that will complete when all of the `Task` 
// objects in an enumerable collection have completed. 
await Task.WhenAll(tasks);
Nkosi
  • 235,767
  • 35
  • 427
  • 472
2

Now, in .NET 6 there is already a built-in Parallel.ForEachAsync

See: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel.foreachasync?view=net-6.0 https://www.hanselman.com/blog/parallelforeachasync-in-net-6

Pawel Troka
  • 853
  • 2
  • 12
  • 33