0

In the code below (a fairly simple one just for demonstration), my finally clause gets executed when awaiting for the HttpClient to finish. So the output is something like:

Done

StatusCode: 200,...

When I was expecting it to be:

StatusCode: 200,...

Done

  class Program
        {
            private static HttpClient _httpClient = new HttpClient();
            static async Task Main(string[] args)
            {
                var numbers = new List<int> {1};
                try
                {
                    await Task.Run(() =>
                        {
                            Parallel.ForEach(numbers, async (number) => { await Do1(); });
                        });
                }
                finally
                {
                    Console.WriteLine("Done");
                }
    
                Console.ReadKey();
            }
            private static async Task Do1()
            {
                using (var result = await _httpClient.GetAsync("http://www.google.com"))
                {
                    Console.WriteLine(result);
                }
            }
            private static async Task Do2()
            {
                for (int i = 0; i <= 10; i++)
                {
                    Console.WriteLine("Doing" + await Multiply(i));
                }
            }
    
            private static async Task<int> Multiply(int number)
            {
                return await Task.FromResult(number * number);
            }
        }

If I replace my Main method to use the Do2 method then everything works as expected with the following output ("Done" gets printed at the very end):

.

.

.

Doing100

Done

So why is the HttpClient not working as I am expecting it to work? Something that I'd like to mention is that I would like to use the Parallel.ForEach instead of the Task.WhenAll function.

Community
  • 1
  • 1
Shak Ham
  • 1,209
  • 2
  • 9
  • 27
  • 3
    `Parallel.Foreach` only accepts an action, which means when you give it async delegate you end up with an unawaitable async void and nothing waits for that async void delegate to complete before moving on. – JSteward Dec 19 '19 at 15:45
  • Is your goal to execute Do1() in parallel? – Magnus Dec 19 '19 at 15:48
  • JSteward, As long as I'm concerned, the Parallel.ForEach blocks the current thread until all the threads it creates finish before moving forward. The Do1 thread hasn't finished yet, so why is it moving forward? – Shak Ham Dec 19 '19 at 15:52
  • 2
    because you didn't it to wait ... he did wrote it already *nothing waits for that async void delegate* – Selvin Dec 19 '19 at 15:52
  • Magnus, I want to use the Do1() in parallel, but when using the httpclient it doesn't work. If I replace Do1() with Do2(), everything works as I expect. – Shak Ham Dec 19 '19 at 15:53
  • Selvin, I'm not sure I understand. Can you elaborate a little more please? – Shak Ham Dec 19 '19 at 15:54
  • `Parallel.ForEach` does not return a task that you can await. – Magnus Dec 19 '19 at 15:54
  • in fact `Do1` stop blocking when you use `await` from this point the Thread which call `Do1` is free to run other code ... and `Do2` has no `await` so it's not real aync call (you prolly have warning) – Selvin Dec 19 '19 at 15:55
  • Magnus, why does Do2 work differently? In Do2 I'm awaiting too and the Finally doesn't get executed until everything finishes. – Shak Ham Dec 19 '19 at 15:57
  • 1
    @ShakHam you're using the wrong class. `Parallel.ForEach` is meant for data parallelism, not concurrent calls. It doesn't support asynchronous methods *precisely* because it's not meant for concurrent/asynchronous work. It has no overloads that accept a `Func`. Your calls are essentially `async void` calls that nobody awaits – Panagiotis Kanavos Dec 19 '19 at 16:00
  • BTW there are a *lot* of duplicate questions, at least 3 of them answered by me, that explain this, explaining the *correct* classes for this job and including examples using eg ActionBlock or Channels – Panagiotis Kanavos Dec 19 '19 at 16:01
  • Panagiotis Kanavos what are your thoughts regarding Gabriel Luci suggestion? To use https://github.com/dotnet/corefx/issues/34233 – Shak Ham Dec 19 '19 at 16:03
  • Panagiotis Kanavos, can you add a link to your answers, please? I'm not that familiar with ActionBlock or Channels – Shak Ham Dec 19 '19 at 16:04
  • I appreciate all of your help guys! I saw the answers that are related to this. – Shak Ham Dec 19 '19 at 16:08
  • 1
    I was typing up an answer to clarify why `Do2` works differently, but the question got closed. The short answer is that it's not doing anything asynchronous. If `await` sees a completed `Task`, then execution continues synchronously. And your `Multiply` method always returns a completed `Task`. – Gabriel Luci Dec 19 '19 at 16:10
  • Gabriel Luci, what are your suggestions on the DOP parameter? – Shak Ham Dec 19 '19 at 16:19
  • What do you mean by "DOP parameter"? – Gabriel Luci Dec 19 '19 at 16:22
  • @GabrielLuci DOP = Degree Of Parallelism – Theodor Zoulias Dec 19 '19 at 16:48

0 Answers0