-2

I know this is old and there are a lot of articles and questions around however still it is confusing to understand, I have a simple basic example can someone explain it in a simplified way, basically I need to understand the flow.

I have two examples one with await and one without await, when I execute both, the output is identical, I believe I need to have 'Task.Run()' in order to execute asynchronously.

internal class Common
    {
        public async Task Process()
        {
            Console.WriteLine("Process Started");
            DownloadileAsync();
            Console.WriteLine("Process Completed");
        }

        public async Task DownloadileAsync()
        {
            DownloadFile();
            Console.WriteLine("DownloadAsync");
        }

        public async Task DownloadFile()
        {
            Thread.Sleep(2000);
            Console.WriteLine("File Downloaded");
        }
    }

=============================================================================

// one with await

internal class Common
    {
        public async Task Process()
        {
            Console.WriteLine("Process Started");
            await DownloadileAsync();
            Console.WriteLine("Process Completed");
        }

        public async Task DownloadileAsync()
        {
            await DownloadFile();
            Console.WriteLine("DownloadAsync");
        }

        public async Task DownloadFile()
        {
            Thread.Sleep(2000);
            Console.WriteLine("File Downloaded");
        }
    }

// Output 

Main Thread Started
Process Started
File Downloaded
DownloadAsync
Process Completed
Main Thread Completed
Roman
  • 11,966
  • 10
  • 38
  • 47
Mysterious288
  • 365
  • 5
  • 24
  • 4
    Because you used `Thread.Sleep` that blocked the whole thread. There is no asynchronous operation in your example. – shingo Jun 10 '22 at 05:28
  • 1
    When you fix you code (thus use `await Task.Delay`) you can look at [SharpLab.IO](https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQAwAIpwKwG5nKzoCyA3oQMyYAcmAbOgAoBOA9gMYCmAztwILcAngDt2ACgCUycknRzMcAJxiARCw49+Q0egDKAFwCGzfZ2AqJ+WfKiL0AEVYB3YQBtWh4ADEAlq84CIuKWyPIKympsXLyBOgDCrAC2AA7+puYhSAC+BChUUAAsTFGaktKhNkqq6tHcekYmZhZWYY4u7p6+/rHBLZURNZroCSlpTZk5SJQ09A7Obh7efgHaweXWcrb0AHT2nK6GgmIAKj6JnLrJhsLbXmyJupzsrMLA3GJwEplhWBFdnHN2otxlZJlkgA==) to see how the state machines are generated by the compiler. Not awaiting it will not generate a state machine, thus "fire and forget" the async method. – JHBonarius Jun 10 '22 at 06:57

1 Answers1

1

The output is same for both approaches because you block the thread usign Thread.Sleep(2000) in first approach. If you want it to be really async replace it with await Task.Delay(2000) and you will see the difference in output.

Using this:

public async Task Process()
{
     Console.WriteLine("Process Started");
     DownloadileAsync();
     Console.WriteLine("Process Completed");
}

your just start the task and don't wait for it to be finished before executing the rest code after this task.

Imagine you have some async method that returns result:

public async Task<int> GetResultAsync()
{
    // instead of Thread.Sleep(2000); to avoid blocking
    await Task.Delay(2000); // to simulate something like request to another API

    return 10;
}

Having this method your approach without await won't work here:

public async Task Process()
{
     Console.WriteLine("Process Started");
     var result = GetResultAsync();
     Console.WriteLine("Process Completed");

     // prints System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[System.Int32,Test.Enums.Program+<GetResultAsync>d__1]
     // not 10
     // because type of the result is Task<int>, not just int
     Console.WriteLine($"Result is: {result}");
}

So here you don't wait the task to be finished. You just start the task and forget about it (fire-and-forget).

When you have await:

public async Task Process()
{
     Console.WriteLine("Process Started");
     var result = await GetResultAsync(); // await here
     Console.WriteLine("Process Completed");

     // prints 10
     Console.WriteLine($"Result is: {result}");
}

you asynchronously wait for task to be finished before continuation.

Roman
  • 11,966
  • 10
  • 38
  • 47
  • Thanks that is helpful however I would like to understand the flow, like Process calls => DownloadAsync => DownloadFile so I believe there is one main thread that starts from Process and in DownloadFile it gets stuck because of Thread.Sleep, in my code DownloadileAsync(), I thought it will be fire and forget, so would like to understand how many threads are created, and it's flow, one is the main thread and others are worker threads. – Mysterious288 Jun 10 '22 at 06:00
  • @Mysterious288, `Process calls => DownloadAsync => DownloadFile so I believe there is one main thread that starts from Process and in DownloadFile it gets stuck because of Thread.Sleep` - you are right. `I thought it will be fire and forget, so would like to understand how many threads are created` - no thread is created here. When you change to `Task.Run(() => DownloadileAsync());` then it will be fire and forget even with `Thread.Sleep(2000);` because now this is executed in other thread. – Roman Jun 10 '22 at 06:14
  • 1
    @Mysterious288 Threads are not Tasks and Tasks and not Threads. I.e. even if you have one thread, you can have many tasks. Don't mix the concepts up. The generation of threads and the TaskScheduler are not trivial. – JHBonarius Jun 10 '22 at 07:01
  • @JHBonarius I'm not mixing the concepts, it's clear to me, it's just that I'm not understanding the flow, when it is fire and forget and when it is not. – Mysterious288 Jun 10 '22 at 12:33
  • Stephen Cleary writes good blog posts about this: [example](https://blog.stephencleary.com/2012/02/async-and-await.html). Also check the microsoft blogs [example](https://devblogs.microsoft.com/pfxteam/asyncawait-faq/?WT.mc_id=DT-MVP-5000058) – JHBonarius Jun 10 '22 at 13:39