I'm new in the use of asynchronous methods in C#. I have read that these keywords async
and await
help to make the program more responsive by asynchronizing some methods. I have this snippet :
First Way
public static void Main()
{
Console.WriteLine("Hello!! welcome to task application");
Console.ReadKey();
Task<string> ourtask = Task.Factory.StartNew<string>(() =>
{
return "Good Job";
});
ourtask.Wait();
Console.WriteLine(ourtask.Result);
Console.ReadKey();
}
Second Way
public static void Main()
{
Launch();
}
public static async void Launch()
{
Console.WriteLine("Hello!! welcome to task application");
Console.ReadKey();
Console.WriteLine(await GetMessage());
Console.ReadKey();
}
public static Task<string> GetMessage()
{
return Task.Factory.StartNew<string>(() =>
{
return "Good Job";
});
}
I need to know :
Is there a difference between the two implementations (in the concept of parallelism)?
What are the benefits of using
async
andawait
keywords if I can just create a task and wait for it to finish?
async / await cleans up the masses of complicated code that would over utilize Task.ContinueWith.ContinueWith.ContinueWith and so on.
From a coding perspective it is much harder to visualize, debug and maintain Task.ContinueWith, including the associated exception handling that must come with it.
So, await came along, and gave us this
Which is pretty much equivalent to:
You can see from the example that everything after GetMessage() is contained in a ContinueWith, however the method returns the task as soon as it is created. So it is returning to the calling method.
Here we need to wait on that Task, otherwise the program will continue to exit:
Not having to write ContinueWith() means our code becomes more readable, especially in the cases of when we have to chain multiple await blocks together in a single method, it will "read" fine.
Also as mentioned before, better exception handling is dealt with the await examples, otherwise we would have to use TPL methods to deal with exceptions, which can also over complicate a code base.
With regards to both of your examples, they are not really equivalent, so you can't really judge one over the other. However, async/await is equivalent to constructing Tasks/ContinueWith.
I see async/await as an evolution of TPL into the actual language itself. A type of syntactical sugar.
We have two main benefits of async/await programming
1- The nonblocking programming
when you have long-running operations that do not require to block the execution. In this case, you can perform other work while waiting for the result of the long-running task.
Imagine that we have two program flow and they can work in parallel without blocking each other.
Example: Let's say that we need to log every error appear but at the same time this should not block the flow so in that case we can log and return message at the same time.
2- the benefit of thread management in async/await programming
we know that in normal programming (blocking), every line of code is blocking everything after it until it finishes the process even if we have different flows (two flows without any dependency). but in async/await programming, the application will not block this thread, in other words, they will release it to do another work and when the function finishes the work any free thread will handle the response.
C# async and await: Why Do We Need Them?
Say you have a single border checkpoint. Each car can pass it one-by-one to have customs take a look at their car to see if they're not smuggling any Belgian chocolate.
Now assume that you are in line in your Volkswagen Beetle where you can barely fit in and before you is a 24-wheel monstertruck. You are now stuck behind this behemoth for a long time until customs are done searching through it all before they can move on to you who they basically just have to pat down to tell you you're good to go.
In order to combat this efficiency, our good friends at the border patrol have an idea and install a second checkpoint. Now they can pass in twice as many people and you can just take that one instead of waiting behind the monstertruck!
Problem solved, right? Not exactly. They forgot to create a second road that leads to that checkpoint so all traffic still has to go over the single lane, resulting in the truck still blocking the Beetle.
How does this relate to your code? Very easy: you're doing the same.
When you create a new
Task
you essentially create that second checkpoint. However when you now synchronously block it using.Wait()
, you are forcing everyone to take that single road.In the second example you use
await
which creates that second road and allows your car to be handled simultaneously with the truck.I'll attempt to answer the questions directly:
Neither of your examples (effectively) involves any parallelism. I see 2 main differences between them: 1) The first example will block a thread while the task runs on a second thread, which is pointless, and 2) the second example will exit early. As soon as
await
is encountered, control immediately returns toMain()
, and since you're not waiting for the task returned fromLaunch()
to complete, your program will exit at that point.The benefit of using
async
andawait
vs. waiting for a task to complete is thatawait
does not block the current thread while that task is running. Under the hood, anytime the compiler encounters anawait
, it effectively rewrites the rest of that method as a callback that will be called upon completion of the task. That frees up the current thread to do other things while the task is running, such as respond to user input in a client app or service other requests in a Web application.Frankly, this is not a good example to demonstrate the benefits of
async
/await
. You're basically saying that you want to do CPU-bound work, and you don't want to do anything else until that work is done. You may as well do that synchronously. Asynchrony really shines when doing I/O-bound work, such as making a call across the network (using a properly implemented asynchronous library such as HttpClient), because you're not simply trading one thread for another as in your second example; there literally is no thread being consumed by that I/O-bound work.As others have alluded to, parallelism is another topic entirely. While
async
/await
can be useful contructs to help you achieve it, there's a bit more involved, and in my opinion you'd be better served to get a firm grasp on the thread-freeing benefits before "moving on" to parallelism.Also as others have alluded to, this is a big topic and I highly recommend you check out some of the great resources out there. Since I already referenced Stephen Cleary's blog, I'll go ahead and give it a full plug - his async/await intro and subsequent posts are an excellent primer on the subject.