Update with citations
Since my answer has been downvoted for reasons I am not aware of, since downvoters did not give any reasons for the downvote, except for Rob, I am here to defend my answer.
The point here is not to prove anyone is wrong but to make sure OP receives correct information and facts.
So what was OPs question? Two questions in my opinion:
- Can someone help me understand the working of Async method in C#? (Explicity asked)
- Why is my code not doing what I think it should be doing?
There were many answers posted to fix the code. Nelson's answer has correct code as well, hence I never downvoted. My issue is not with the solutions provided here but with the statement that "Asynchronous is NOT concurrent". I also wanted to explain how async-await works, using plain English, since the OP explicitly asked for this. So here is why I still stand my ground:
Stephen Cleary, a well respected author and SO member states this in his book on concurrency:
Asynchronous Programming
A form of concurrency that uses futures or callbacks to avoid
unnecessary threads.
A future (or promise) is a type that represents
some operation that will complete in the future. The modern future
types in .NET are Task and Task. Older asynchronous APIs use
callbacks or events instead of futures. Asynchronous programming is
centered around the idea of an asynchronous operation: some operation
that is started that will complete some time later. While the
operation is in progress, it does not block the original thread; the
thread that starts the operation is free to do other work. When the
operation completes, it notifies its future or invokes its completion
callback event to let the application know the operation is finished.
He goes on further to say:
Asynchronous programming is a powerful form of concurrency, but until recently, it required extremely complex code. The async and await support in VS2012 make asynchronous programming almost as easy as synchronous (nonconcurrent) programming.
Here is a stackoverflow answer wherein the same author has answered another similar question.
This is the link to the book and the part of I have quoted is from 1.1. Introduction to Concurrency.
Now that should clarify and support my statement on asynchronous being concurrent.
I am not sure how my "examples have nothing to do with concurrency" but perhaps Rob can chime in and explain that.
I am not sure what the other misleading parts are in my answer so I cannot defend myself. I DO NOT think there are any misleading statements I have made but if I have, I, the OP and other members would love to know them so it can benefit as all.
End of Update with citations
.
It is NOT true as some other answers have stated that async is not a form of concurrency. Ansynchronous programming IS (Yes it is) a form of concurrency. But, having said that, it does not mean the work will need less time. The work will still take the same amount of time (possibly longer due to context switching). But the trick is the work will be done at the same time without waiting for previous work to finish (Delay1 then Delay2 and then Delay3). Let me explain all this using your code.
Here is what you are doing in your code in Print method: Lets call the thread executing Print P1
- Do Delay1 AND when it returns then go to 2 below. In Delay1 method another thread is given the task to sleep. Lets call it T1. T1 please sleep for 5 secs and then return "hello".
- Do Delay2 AND when it returns then go to 3 below. In Delay2 method another thread is given the task to sleep. Lets call it T2. T2 please sleep for 5 secs and then return "hello".
- Do Delay3 AND when it returns then go to next line (there is no next line so program will complete). In Delay3 method another thread is given the task to sleep. Lets call it T3. T3 please sleep for 5 secs and then return "hello".
So your print method method calls Delay1 and waits for it to return. Then it calls Delay2 and waits for it to return. Then 3 and waits for it to return. All those will return after 5 secs of sleeping (each).
So you may ask: "then what is the point of all this"?
The point is that in your print method you can call all of them at the same time: do Delay1, do Delay2 and do Delay3 and when they are all done then go to next line.
So what does that get you?
Well if you were calling some web service in your Delay methods and each one takes 10 seconds to return, you are calling them ALL AT THE SAME time (almost) so all three will start getting to work. Chances are they will all return after a little over 10 seconds. So you basically sent them all to work at the same time hence "Concurrency". If you do not call them asynchronously, then Delay1 needs to complete (10 Secs), then Delay2 (10 secs) then Delay3 (10 secs) so 30 seconds all in total.
Think about it like this:
You are a manager at McDonald's. You have many employees (thread pool) and you need them to do work (Delay1, Delay2 and Delay3). What is better to tell 1 employee to do Delay1 then Delay2 and then Delay3 or assign 3 different employees (threads from thread pool) and ask 1 employee to do Delay1 and tell second employee to do Delay2 and not to wait for Delay1 to complete and ask 3rd employee to do Delay3. Obviously you would choose 3 employees.
Now in the world of thread pools it is a bit different. It is possible that the the same thread is chosen for 2 tasks because one thread did the job and came back to the pool and then was given another task. In your example, it is highly unlikely because they need to sleep for 5 secs. There is only 1 way to do that.
Please note in some cases, one task depends on another and you have no choice but to wait. For example, if Delay1 was getting a file from a remote machine and Delay2 was getting another file, and Delay3 would process both files then Delay1 and Delay2 can be done "Concurrently" but Delay3 will need to wait for them to return before it can start.
And to avoid confusion, please note that in your case T1, T2 and T3 are only doing the sleep. The returning of hello will be done by the P1 thread. There are ways so that P1 does not have to do this but in your case control will be give to P1 once sleeping is done.