简体   繁体   中英

Benefits of using async and await keywords

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 :

  1. Is there a difference between the two implementations (in the concept of parallelism)?

  2. What are the benefits of using async and await keywords if I can just create a task and wait for it to finish?

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:

  1. 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 to Main() , and since you're not waiting for the task returned from Launch() to complete, your program will exit at that point.

  2. The benefit of using async and await vs. waiting for a task to complete is that await does not block the current thread while that task is running. Under the hood, anytime the compiler encounters an await , 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.

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?

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

    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";
            });
    }

Which is pretty much equivalent to:

    public static void Main()
    {
        Launch();
    }
    public static async void Launch()
    {
        Console.WriteLine("Hello!! welcome to task application");
        Console.ReadKey();
        return  Task.Factory.StartNew(() => GetMessage())
            .ContinueWith((t) => 
                  {
                     Console.WriteLine(t.Result)
                     Console.ReadKey();
                  });
    }

    public static Task<string> GetMessage()
    {
        return Task.Factory.StartNew<string>(() =>
            {
                return "Good Job";
            });
    }

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:

Launch().Wait();

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.

Going with the border checkpoint analogy , I would say:

Sync border employees

You have 4 incoming lanes and 2 officers. The officer handling the incoming monstertruck, Jack, does not know the exact rules for monstertrucks, so he calls the office. Now, if he works sync, he stays on the phone waiting for a response. So he is not able to handle anything else - his colleague Mary will have to do that.

Fortunately Mary works parallel, so she can process the 3 unblocked lanes in the meantime. However, because she also works sync, she only one processes one vehicle at a time. So when she has to check the rules for a motorcycle with a cheetah in the sidecar, she has to call the main office and also stays on the phone for the answer.

Now we have two lanes blocked by pending jobs, and two lanes blocked because there are no employees.

Async border employees

Now if Jack works async - he will not hold the line waiting for a response. Instead he hangs up, and goes to the second lane, and processes another car, while waiting for the main office to return his call. Because the second lane is a very nervous lady with a stutter, this is taking him quite some time.

But fortunately Mary now also works async, and when she finished processing her second vehicle (or paused it, because she had to check on the cheetah), she can take the return call from the office on the monstertruck. So she can finish processing the monstertruck.
But of course the monstertruck is not gone right after she finished - the driver has log his time spent at the checkpoint. Fortunately Mary still works parallel, so she can start processing a car in another lane.

Costs

Then one day, the Burning Man festival is starting, and a lot of unusual vehicles arrive at the border. These all take a lot of calls to the office, therefore blocking all 4 lanes. So Jack and Mary can only sit around waiting for return cars, while the line of vehicles is growing.

Fortunately, land is cheap in this area, so their boss decides to add 4 more lanes. While some of these extra lanes are also blocked waiting for return calls from the office, at least Jack and Mary keep busy, and don't have to sit and wait for calls. Of course their boss can consider hiring some extra employees to reduce the traffic jam, but he knows they will need housing and training, and the festival will be over soon, so he leaves it as it is...

Recap

Leaning towards ASP.Net:

  • lanes (eg connections) are cheap, and can be scaled easily (and you should probably increase the connection limit and queue size in IIS for async, eg see introduction-reference below)
  • employees (threads) are more expensive (eg see introduction-reference below))
  • 'slow' parts of a job (eg I/O, or remote http-requests), should not block your expensive employees (ie one should use async)
  • NB: jobs may change employees (pun intended), so don't keep data with your employees. Eg Jack should not put monstertruck-papers in his pocket, because Mary may process it further - he should rather return it, or keep it in a job-dependent storage (don't store data within the thread, but rather in the HttpContext)

Literature

FWIW: I like this technical introduction from 2014 by Stephen Cleary .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM