2

I am doing a little research to understand async / await of C# better.

I found a web site that has the following code to show how much slower synchronous processing is vs async / await:

 public IActionResult Index()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();

            ContentManagement service = new ContentManagement();
            var content = service.GetContent();
            var count = service.GetCount();
            var name = service.GetName();

            watch.Stop();
            ViewBag.WatchMilliseconds = watch.ElapsedMilliseconds;

            return View();
        }

        [HttpGet]
        public async Task<ActionResult> IndexAsync()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();

            ContentManagement service = new ContentManagement();
            var contentTask = service.GetContentAsync();
            var countTask = service.GetCountAsync();
            var nameTask = service.GetNameAsync();

            var content = await contentTask;
            var count = await countTask;
            var name = await nameTask;

            watch.Stop();
            ViewBag.WatchMilliseconds = watch.ElapsedMilliseconds;
            return View("Index");
        }


        public class ContentManagement
        {
            public string GetContent()
            {
                Thread.Sleep(2000);
                return "content";
            }

            public int GetCount()
            {
                Thread.Sleep(5000);
                return 4;
            }

            public string GetName()
            {
                Thread.Sleep(3000);
                return "Matthew";
            }
            public async Task<string> GetContentAsync()
            {
                await Task.Delay(2000);
                return "content";
            }

            public async Task<int> GetCountAsync()
            {
                await Task.Delay(5000);
                return 4;
            }

            public async Task<string> GetNameAsync()
            {
                await Task.Delay(3000);
                return "Matthew";
            }
        }

I understand the above code at a high level and why it performs faster.

What I don't understand is if threads are not being used, how is the processing running at the same time?

I have read in a couple of places that async / await does not create new threads to do the processing. So, what is async / await doing to allow processing to happen at the same time? The three await Task.Delay are running in parallel, correct? If it is not creating 3 threads, what is it doing?

I just want to understand what is happening at a high level.

Let me know.

Thanks in advance.

Richard
  • 1,054
  • 1
  • 19
  • 36
  • Possible duplicate of [How does await async work in C#](https://stackoverflow.com/questions/17488677/how-does-await-async-work-in-c-sharp) – Clint Jun 03 '17 at 23:52

2 Answers2

5

if threads are not being used, how is the processing running at the same time?

Threads let you parallelize computations on the same system. When communications or other I/O are involved, there is a different system with which your code communicates. When you initiate the task, the other system starts doing work. This happens in parallel to your system, which is free to do whatever else it needs to do until you await the task.

The three await Task.Delay are running in parallel, correct?

They are not exactly running, they are sleeping in parallel. Sleeping takes very little resources. That's why they appear to be "running" in parallel.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Thanks for responding. Since the await Task.Delay are not exactly running, is this demo a true example of the performance gains using async / await? – Richard Jun 04 '17 at 00:19
  • If processing was doing something more realistic like three calls to HttpClient . GetStringAsync. Something like below:HttpClient client = new HttpClient(); Task getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); string urlContents = await getStringTask; Would I see a performance increase? – Richard Jun 04 '17 at 00:26
  • @Richard Three calls to `HttpClient` are still spending most of their time waiting for the work to be done by HTTP server and for data transfer to be completed by the network interface. Hence, you may see a significant performance increase, comparable to what you see with the delay. – Sergey Kalinichenko Jun 04 '17 at 00:33
  • @Richard If, on the other hand, your tasks were to perform something CPU-intensive, or if they were calling HttpClient synchronously, then there would be no performance gain. – Sergey Kalinichenko Jun 04 '17 at 00:34
  • I think the answer you gave to my first question finally clicked. So if I had something like this: var contentTask = service.GetContentAsync(); DoIndependentSyncWorkOnMySystem(); var content = await contentTask; The system related to service.GetContentAsync would work in parallel to my system doing DoIndependentSyncWorkOnMySystem(); Correct? – Richard Jun 04 '17 at 00:48
  • @Richard Absolutely - `GetContentAsync` would be working on the server while your `DoIndependentSyncWorkOnMySystem` would keep your system busy. – Sergey Kalinichenko Jun 04 '17 at 00:51
  • Great. Thank you so much – Richard Jun 04 '17 at 01:01
2

What I don't understand is if threads are not being used, how is the processing running at the same time?

You can think of it as an event firing when the operation is complete, as opposed to a thread being blocked until the operation is complete.

I have read in a couple of places that async / await does not create new threads to do the processing.

async and await do not; that is true. For more about how async and await work, see my intro post.

So, what is async / await doing to allow processing to happen at the same time?

One of the primary use cases of async/await is for I/O-based code. I have a long blog post that goes into the details of how asynchronous I/O does not require threads.

The three await Task.Delay are running in parallel, correct?

I prefer to use the term "concurrently", just to avoid confusion with Parallel and Parallel LINQ, both of which were created for CPU-bound parallelism and do not work as generally expected with async/await. So, I would say that both parallelism and asynchrony are forms of concurrency, and this is an example of asynchronous concurrency.

(That said, using the term "parallel" is certainly in concord with the common usage of the term).

If it is not creating 3 threads, what is it doing?

Task.Delay is not an I/O-based operation, but it is very similar to one. It uses timers underneath, so it's completely different than Thread.Sleep.

Thread.Sleep will block a thread - I believe it does go all the way to an OS Sleep call, which causes the OS to place the thread in a wait state until its sleep time is expired.

Task.Delay acts more like an I/O operation. So, it sets up a timer that fires off an event when the time expires. Timers are managed by the OS itself - as time proceeds forward (clock ticks on the CPU), the OS will notify the timer when it has completed. It's a bit more complex than that (for efficiency, .NET will coalesce managed timers), but that's the general idea.

So, the point is that there is no dedicated thread for each Task.Delay that is blocked.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thanks for this explanation and the links to your other posts. I will be sure to read them. This information was very helpful. Especially the difference between Task.Delay (it makes sense that it uses a timer) and Thread.Sleep. – Richard Jun 06 '17 at 00:02