0

I'm working on a problem where I have to delete records using a service call. The issue is that I have a for each loop where i have multiple await operations.This is making the operation take lot of time and performance is lacking

foreach(var a in list<long>b)
    {
     await _serviceresolver().DeleteOperationAsync(id,a)
    }
ceng
  • 128
  • 3
  • 17
  • You could create a list of the tasks and just add the tasks to the list inside the loop instead of awaiting them. Then after the loop do `await Task.WhenAll(tasks);` – itsme86 May 06 '20 at 16:54
  • You can use PLINQ (parallel linq) – Marlonchosky May 06 '20 at 16:54
  • 1
    You assume that the async is your problem. IT IS NOT - the delete operation just takes a certain time. If you would do it sync (which you essentially do) the time would not change. – TomTom May 06 '20 at 16:56
  • @itsme86 how can i do that if you can be little more specific – TestforAngular May 06 '20 at 17:01
  • @Marlonchosky can you tell me how can i do that – TestforAngular May 06 '20 at 17:01
  • @TestforAngular I posted an answer with what you're looking for. – itsme86 May 06 '20 at 17:08
  • @TestforAngular sure, I wrote an answer. If it's not clear, asking me anything. I hope it helps! – Marlonchosky May 06 '20 at 17:10
  • At heart async is about multi-tasking, so wrt disk io it is pretty much meaningless. – TaW May 06 '20 at 17:53
  • @TestforAngular if this is a question about the performance, I think it probably means you actually are asking how to run multiple async operations in parallel (simultaneously). Rewording the title and description to ask how to request (and await) multiple async operations in parallel would be more on target. I think this is leading to some confusion in those reading the question. – Appurist - Paul W May 06 '20 at 18:11
  • https://learn.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8#a-tour-through-async-enumerables – Alexan May 06 '20 at 18:18

4 Answers4

1

The issue is that I have a for each loop where i have multiple await operations. This is making the operation take lot of time and performance is lacking

The number one solution is to reduce the number of calls. This is often called "chunky" over "chatty". So if your service supports some kind of bulk-delete operation, then expose it in your service type and then you can just do:

await _serviceresolver().BulkDeleteOperationAsync(id, b);

But if that isn't possible, then you can at least use asynchronous concurrency. This is quite different from parallelism; you don't want to use Parallel or PLINQ.

var service = _serviceresolver();
var tasks = b.Select(a => service.DeleteOperationAsync(id, a)).ToList();
await Task.WhenAll(tasks);
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Agreed, `Task.WhenAll` is probably the best general-purpose C# solution; `BulkDeleteOperationAsync` is probably better in this case if you can use it. Just be aware of `Task.WaitAll` exists as well and is not the same thing. WhenAll is async to the caller, `WaitAll` is blocking. `WhenAll` is better if it's a case where you can use it. – Appurist - Paul W May 06 '20 at 22:06
0

I do not know what code is behind this DeleteOperationAsync, but for sure async/await isn't designed to speed things up. It was designated to "spare" threads (colloquially speaking)

The best would be to change the method to take as a parameter the whole list of ids - instead of taking and sending just one id.

And then to perform this async/await heavy operation only once for all of the ids.

If that is not possible, you could just run it in parallel using TPL (but it is ready the worst-case scenario - really:) )

Parallel.ForEach(listOfIdsToDelete, 
async idToDelete => await _serviceresolver().DeleteOperationAsync(id,idToDelete)
);
Piotr
  • 1,155
  • 12
  • 29
  • so this operation will be faster than what i have or it will be a performace issue?? – TestforAngular May 06 '20 at 17:08
  • Yeah, it will be faster, but as i said. The best option - you should pursue is to change (or add as additional one) this `DeleteOperationAsync` to `DeleteOperationBulkAsync` – Piotr May 06 '20 at 17:12
0

You're waiting for each async operation to finish right now. If you can fire them all off concurrently, you can just call them without the await, or if you need to know when they finish, you can just fire them all off and then wait for them all to finish by tracking the tasks in a list:

List<Task> tasks = new List<Task>();

foreach (var a in List<long> b)
    tasks.Add(_serviceresolveer().DeleteOperationAsync(id, a));

await Task.WhenAll(tasks);
itsme86
  • 19,266
  • 4
  • 41
  • 57
0

You can use PLINQ (to leverage of all the processors of your machine) and the Task.WhenAll method (to no freeze the calling thread). In code, resulting something like this:

class Program {
    static async Task Main(string[] args) {
        var list = new List<long> {
            4, 3, 2
        };

        var service = new Service();
        var response = 
            from item in list.AsParallel()
            select service.DeleteOperationAsync(item);
        await Task.WhenAll(response);

    }
}

public class Service {
    public async Task DeleteOperationAsync(long value) {
        await Task.Delay(2000);
        Console.WriteLine($"Finished... {value}");
    }
}
Marlonchosky
  • 494
  • 5
  • 16