13

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?

Ellis
  • 173
  • 13
Lamloumi Afif
  • 8,941
  • 26
  • 98
  • 191
  • 2
    Async and Paralellism are two different things, though you can achieve parallelism (in some scenarios) via async (among other ways) they are distinct concepts and should not be confused. – Erik Funkenbusch Mar 03 '15 at 20:34
  • @ErikFunkenbusch what are the differences between parallelism and Async?? – Lamloumi Afif Mar 03 '15 at 20:37
  • @LamloumiAfif: I think that question is too broad to answer in a StackOverflow post. Have you spent some time reading up on the topic? – StriplingWarrior Mar 03 '15 at 20:38
  • Async is strictly about scalability. A block of code can give up it's thread and wait for a condition, and that thread can go do something else until that condition is satisfied, then that code is resumed (possibly from a different thread). Parallelism is about executing code concurrently. – Erik Funkenbusch Mar 03 '15 at 20:39
  • 3
    You should spend some time reading some tutorials/resources on these concepts, as they are very complex topics with a lot of intricacies; explaining everything about them is *well* beyond the scope of an SO question, let alone a comment. Entire books are written on the subject. – Servy Mar 03 '15 at 20:39
  • 1
    @ErikFunkenbusch Asychronous code isn't *strictly* about scalability. That's just one facet of it. – Servy Mar 03 '15 at 20:40
  • http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference is a good place to start. – StriplingWarrior Mar 03 '15 at 20:41
  • @Servy - While you can achieve other benefits from asynchrony, such as parallelism in certain cases, this is largely just a side-effect of the primary purpose of async which is to increase scalability via IOCP utilization. Yes, there are many benefits or aspects you can gain, but all that is really just icing on the cake in my opinion. – Erik Funkenbusch Mar 03 '15 at 20:46
  • @ErikFunkenbusch the scaling benefits you're talking about are only meaningful in situations that are highly parallelized. There are reasons to use asynchronous *beyond* parallelization, such as responsiveness. – Servy Mar 03 '15 at 20:49
  • @Servy - Those are the "other benefits" I referred to. Responsiveness is a function of scalability. If all your threads are blocked waiting, they won't respond. Thus your software isn't scaling. I think we're having a problem with terminology here. I use "scalability" to mean "fully utilizing all resources", which responsiveness is a part of. – Erik Funkenbusch Mar 03 '15 at 21:07
  • 1
    @ErikFunkenbusch It's entirely possible (and common) to want to have an entirely single threaded application when dealing with a desktop application. This is only possible if the application is asynchronous. It involves no parallelism at all. It has nothing to do with "scaling". – Servy Mar 03 '15 at 21:09
  • @Servy - Where did you get the idea I said it had something to do with parallelism? I explicitly said otherwise, that async and parallelism are distinctly different concepts. – Erik Funkenbusch Mar 03 '15 at 21:12
  • Making a desktop application asynchronous has nothing to do with resource utilization, it has nothing to do with how well it scales. It allows the UI to be responsive. In many cases this actually makes the application scale *more poorly*. It consumes *more* resources to be asynchronous, but it provides a superior user experience. Yes, async and parallelism are different concepts, and I realize that you said as much, but the benefits of scalability only apply in asynchronous context that *involve parallelism*. – Servy Mar 03 '15 at 21:15
  • Ah, where's my popcorn? – Mikko Viitala Mar 03 '15 at 21:15
  • 8
    Parallelism is when you hire two cooks, one to cook the eggs and one to toast the bread. Asynchrony is when one cook checks the eggs, then checks the toast, then serves the eggs, then serves the toast. Asynchrony can be parallel, but does not require parallelism. – Eric Lippert Mar 04 '15 at 02:00
  • @EricLippert So In this case parallelism is more efficient to make an application more responsive than Asynchrony. For example, in my desktop application I have to use parallelism instead of Asynchrony to load data and display it in the UI – Lamloumi Afif Mar 04 '15 at 06:29
  • 1
    @LamloumiAfif: No, parallelism improves application performance, not responsiveness. The way to improve application responsiveness is via asynchrony. You can maximize the responsiveness of the UI thread by not running code on the UI thread. Here, I define responsiveness as, "how quickly the application responds to user input" and performance as "how quickly the application completes a task." – Brian Mar 04 '15 at 14:21
  • 1
    @LamloumiAfif: You are drawing a conclusion that I did not intend to imply. You talk about *efficiency*. Efficiency is *benefit derived divided by cost*. The costs of parallelism are extremely high; to get true parallelism you need to dedicate *an entire CPU* to the problem. There are definitely cases where that is a good price to pay. But for mere application responsiveness, you don't need an entire CPU just sitting there waiting for the user to type something. You can do a little bit of work, then process user input, then do a bit of work, like our first cook. Only one CPU required. – Eric Lippert Mar 04 '15 at 14:34
  • 5
    To more clearly define the difference, without an analogy to cooks: parallelism is when you have two jobs being done at the same time. Asynchrony is when you have to wait for something, and you decide to do other work while you are waiting. Parallelism is one technique for achieving asynchrony, but it is not the only one. In your example code you *defeat* the benefits of asynchrony by saying "I wish to synchronously wait on this asynchronous operation"; that's what the `Wait` call means. – Eric Lippert Mar 04 '15 at 16:12

5 Answers5

68

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.

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • 4
    This example doesn't represent either situation well,, nor does it do anything to answer the actual question being asked. Neither situation ever involves any parallelism, which is what you're trying to describe here. Your example does nothing to explain (even in an analogy form) what asynchronous is or why someone might want to use it (the analogy is all covering entirely synchronous operations). There's just no technical value in this answer at all. It's doing nothing to explain what's actually going on. Apparently funny stories with no meaningful value are all people are interested in. – Servy Mar 03 '15 at 20:51
  • @Servy: the first bullet in his question asks about the difference in implementation between the two examples, which is what I describe here. If there are technical differences between my analogy and the problem at hand then I'd like to know so I can improve it and learn from it, but the way I see it I do answer the question. Just because the comments get hung up on asychronism vs parallellism, doesn't mean that's the most important aspect. – Jeroen Vannevel Mar 03 '15 at 20:55
  • So **if** you had explained the differences between these two examples you would have successfully answered a fraction of the question asked, while still leaving much of it unanswered. Of course, you aren't explaining the differences between these two programs at all. Just to name one issue, the second program schedules some work to happen in a background thread and then ends the process and tears everything down without doing anything, just to name one of dozens of differences between them. – Servy Mar 03 '15 at 20:59
  • The 2nd program has a bug. I am pretty sure it isn't a design goal for it to work that way. (Change `Launch()` to `Launch().Wait()`) I don't understand the idea of saying the answer is "bad" without explaining why or providing a better answer. – Grax32 Mar 03 '15 at 22:42
  • 25
    @Servy why not post a better answer rather than expending so much energy criticizing this one? – Todd Menier Mar 04 '15 at 15:21
  • 2
    @ToddMenier Because one could write an entire book trying to answer this question and still not cover all of it. The question is way, way, too broad to be an answerable SO question. That's one of the major problems with this answer, is that it doesn't even come close to *beginning* to answer the question, in addition to being full of technical inaccuracies and misinformation. Writing an actual answer would be more or less impossible. – Servy Mar 04 '15 at 15:23
  • @Grax I have explained why, I've explained quite a number of problems with this answer. – Servy Mar 04 '15 at 15:23
  • @JeroenVannevel, I took up the challenge, and tried to improve the example of a/sync border patrol. It was too long for a comment, so I added it as [an answer](https://stackoverflow.com/a/65089079/422877)... – Yahoo Serious Dec 01 '20 at 10:50
11

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.

Todd Menier
  • 37,557
  • 17
  • 150
  • 173
6

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?

Saeed El Dah
  • 355
  • 5
  • 11
4

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.

James Simpson
  • 1,150
  • 6
  • 11
2

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 calls, 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 (e.g. connections) are cheap, and can be scaled easily (and you should probably increase the connection limit and queue size in IIS for async, e.g. see introduction-reference below)
  • employees (threads) are more expensive (e.g. see introduction-reference below))
  • 'slow' parts of a job (e.g. I/O, or remote http-requests), should not block your expensive employees (i.e. one should use async)
  • NB: jobs may change employees (pun intended), so don't keep data with your employees. E.g. 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.

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
Yahoo Serious
  • 3,728
  • 1
  • 33
  • 37