0

Lets say I have a collection of objects that I need to process asynchronously.

List<Customer> customers = GetAllCustomers();

customers.ForEach(async (e) => { await e.Process(); });

I assume these will be processed asynchronously without holding the current thread. What I would like to know is if there is a limit on how many customers can be in the collection. What if it is 100, 000. Will it just get queued up in the asynch queue?

** I'm also not sure if this is the right approach. I don't really need to await the result of each customer processing. I just want to process the customer data for all seamlessly. I don't care if each process fails as it will be picked up again until successful. I could use Tasks, but I would rather not create threads leading to possible racing conditions etc. **

svick
  • 236,525
  • 50
  • 385
  • 514
Alex J
  • 1,547
  • 2
  • 26
  • 41
  • Do you know what task does ? – ilansch Apr 09 '14 at 18:18
  • You're passing an `async void` lambda `Action` into `Parallel.ForEach`. You should be aware that the execution control will instantly return to `Parallel.ForEach` as soon as it hits `await` inside the lambda, and then it's essentially a fire-and-forget call outside `Parallel.ForEach`. Probably not what you are looking for? – noseratio Apr 09 '14 at 20:20
  • as long as it hits each record async, it should be fine. The method is self sufficient and if it fails, it will be picked up for processing again. – Alex J Apr 09 '14 at 20:26
  • @AlexJ, this way `Parallel.ForEach` may return even *before* the 1st `e.Process()` has finished. E.g., if you call `Parallel.ForEach` inside an ASP.NET controller method, the HTTP response may be sent to the client before any of `e.Process()` async calls has chance to complete. I really can't think of any scenarios where it might be acceptable. If this is *still* the behavior you want, you don't need `Parallel.ForEach`, as you don't do any CPU-bound work. Just use regular `foreach` to start your fire-and-forget calls. – noseratio Apr 10 '14 at 01:59

2 Answers2

4

What I would like to know is if there is a limit on how many customers can be in the collection. What if it is 100, 000. Will it just get queued up in the asynch queue?

There is no limit. However, each processing is started one at a time; that is, the Process method (which I assume is asynchronous and should be called ProcessAsync) will be called once for each item, returning a Task<T> for each.

If you do want to (asynchronously) wait for all the tasks to complete, you can use Task.WhenAll as such:

await Task.WhenAll(customers.Select(e => e.Process()));
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • You mentioned the Process method should be called ProcessAsync. What do you mean by it? It is a custom method. It could have been named ProcessCustomer. – Alex J Apr 09 '14 at 20:06
  • 3
    @AlexJ [The Task-based Asynchronous Patterns (TAP)](http://msdn.microsoft.com/en-us/library/hh873175) says that all async methods should have `Async` at the end of their name. This is to make it clear which methods are async (and have to be `await`ed). – svick Apr 09 '14 at 21:18
  • ok, thank you for that explanation. Does the ProcessAsyc method also need async in the definition? as in, public static async Task ProcessAsync(), or that decoration is not required? – Alex J Apr 09 '14 at 21:39
  • 2
    You should only use the `async` keyword if you need to `await` in the method. But any `Task`-returning method should be named with the `Async` suffix. You may find my [`async` intro](http://blog.stephencleary.com/2012/02/async-and-await.html) helpful. – Stephen Cleary Apr 09 '14 at 23:27
  • ok, thank you for that link. Wonderful job on the explanation. One more question I have is if say I have 100 customers in that collection and each does the same thing - upload data somewhere and update db record and i'm using await Task.WhenAll. Say customer 2 object uploads data where it waits two seconds for the server to respond, will customer 1 attempt a db update (assuming it completed the upload) while 2 is waiting? same thing with all other objects in the collection - while some wait, others will continue what they can?. – Alex J Apr 10 '14 at 14:47
  • also, if one of the process method for one customer object throws an exception, will it stop others from proceeding? – Alex J Apr 10 '14 at 14:47
  • They are all asynchronous, and if one completes with an exception, then the `Task.WhenAll` will wait for all of them to complete. There's no automatic cancellation of the other tasks. – Stephen Cleary Apr 10 '14 at 15:28
  • ok cool. How about any in parallel execution where processing customer 1 through N overlaps when some waits and others don't? if customer 1 is waiting on something, 2 can start. Is that correct? or 2 waits until 1 is complete. – Alex J Apr 10 '14 at 20:40
  • Assuming you mean *asynchronous* waits, then 2 will be able to start as soon as 1 awaits. – Stephen Cleary Apr 10 '14 at 20:56
  • Thank you. last question. So does this mean, if each customer process takes 10 seconds to complete, a synchronous loop will complete in 1000 seconds for 100 customers vs asynchronous loop (WhenAll) will complete in say 100 seconds? – Alex J Apr 10 '14 at 21:00
  • If the processing is mostly I/O-bound, then you should expect 10-11 seconds for `WhenAll`. If you want to run some experiments, you can use `Thread.Sleep(1000)` for synchronous code and `await Task.Delay(1000)` for asynchronous code; they both delay for one second but one is blocking/synchronous and the other is asynchronous. – Stephen Cleary Apr 10 '14 at 21:14
  • ok thank you. yes, I will run some tests and report the results. – Alex J Apr 10 '14 at 21:22
0

Tasks are more suited to this type of load, you can set options to control how many threads etc..

 List<Task> tasks = new List<Task>();
 customers.ForEach((e) => { Task task = Task.Run(() =>{ e.Process();  tasks.Add(task);})});

 Task.WaitAll(tasks.ToArray());
T McKeown
  • 12,971
  • 1
  • 25
  • 32
  • task is not asynchronous. http://stackoverflow.com/questions/9519414/whats-the-difference-between-task-start-wait-and-async-await – Alex J Apr 09 '14 at 18:38
  • Task.WaitAll(tasks.ToArray()); will hold the current thread until complete, if i'm not wrong. – Alex J Apr 09 '14 at 18:42
  • @alexJ, yes you are correct. Task.WaitAll() will block the main thread while the other tasks (threads) complete. – T McKeown Apr 09 '14 at 18:44
  • Also from MSDN - The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active. You can use Task.Run to move CPU-bound work to a background thread, but a background thread doesn't help with a process that's just waiting for results to become available. The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. – Alex J Apr 09 '14 at 18:58