3

Here Eric Lippert says:

A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously...

It does work asynchronously right?

To test that, I made a windows form application and handled one arbitrary event. Inside handler, I started a heavy computation. Clearly, it blocks the UI to respond:

this.KeyPress += Form1_KeyPressed;
....
private async void Form1_KeyPressed(object sender, EventArgs e)
{
   for(int i=0; i<int.max; i++)
      ;
}

What am I missing in Eric's answer?

Community
  • 1
  • 1
Hans
  • 2,674
  • 4
  • 25
  • 48
  • 4
    Does your method use `await` at all? The example you've shown doesn't which, if I remember correctly, *should* be giving you a warning that, because of that lack, the method will actually run synchronously. Are you ignoring that warning? – Damien_The_Unbeliever Nov 16 '15 at 08:57
  • 1
    If you only add `async` to your method, you'll get a warning message that a `await`is lacking and the method will run synchronously. Maybe you should take a look at [The zen of async: Best practices for best performance](https://channel9.msdn.com/Events/Build/BUILD2011/TOOL-829T) to (hopefully) get a better understanding. – Oliver Nov 16 '15 at 08:57
  • when you use await means at this line go back from method and continue the execution. when you dont use await the method is forced to go until the last line and thus not really async. – M.kazem Akhgary Nov 16 '15 at 08:59

7 Answers7

15

What am I missing in Eric's answer?

I meant that it works like any other asynchronous method. When you await something in an asynchronous method the remainder of the method is signed up as the continuation of the awaited thing. That is true whether the asynchronous method is void or not.

In your example your code works exactly like an asynchronous method that returns a task. Try changing your method to return a task, and you'll see it behaves exactly the same.

Remember, "async" does not mean "I run concurrently on another thread". It means "the method may return before its action is completed". The points at which it may return before its action is completed are marked with "await". You haven't marked anything with "await".

I suspect you believe the myth that asynchrony requires concurrency. Again: asynchrony simply means that a method can return before its work is done. You start cooking some eggs, the doorbell rings, you go get the package off the porch, you finish cooking the eggs, you open the package. The "cook eggs" and "fetch the mail" jobs are not concurrent -- you never did them at the same time. They are asynchronous.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thank you very much Eric. Can I summarize like this: *It is structurally an asynchronous method (with all other compiler generated things to create state machine) but it behaves synchronously* ? – Hans Nov 17 '15 at 01:32
  • 3
    @Hans: All "async" methods are synchronous *until the first await.* That's the point where the method returns prematurely and signs its remainder up as the continuation of the awaited operation. – Eric Lippert Nov 17 '15 at 02:58
  • 1
    **I suspect you believe the myth that asynchrony requires concurrency!** I LOVE IT! :) Damn, made my day:). – ipavlu Nov 23 '15 at 14:45
6

All the async keyword does is allow you to await an asynchronous operation in your method (and wraps the result in a task).

Every async method runs synchronously until the first await is reached. If you don't await anything then this method (whether it returns a task or doesn't) will run synchronously.

If your method is synchronous you usually don't need to use async await at all. But if you want to offload a CPU intensive operation to a different thread so the UI thread wouldn't be blocked for a long time you can use Task.Run:

await Task.Run(() => CPUIntensiveMethod());
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 1
    @Hans It works asynchronously in the same way a regular task-returning async method works asynchronously. If you don't need to await anything then the method shouldn't be `async` to begin with. – i3arnon Nov 16 '15 at 09:07
  • Or skip the `await` in the `Task.Run` example if you really want fire-and-forget. – Todd Menier Nov 16 '15 at 13:58
  • @ToddMenier I wouldn't do that inside the event handler. – i3arnon Nov 16 '15 at 14:04
5

Only assigning method with async don't make it to call asynchronously.For asynchronously calling it need a method invokation with the await keyword in it's body.

And about this:

A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously...

This means that you can not await THIS method inside another method like this.

await Form1_KeyPressed(this, EventArgs.Empty)

For your code to work you need a method with await keyword like:

private async void Form1_KeyPressed(object sender, EventArgs e)
{
   for(int i=0; i<int.max; i++)
      ;

   // In the body some code like this

   await YourMethod();

}

Updated version

The “Async” Keyword

What does the “async” keyword do when applied to a method?

When you mark a method with the “async” keyword, you’re really telling the compiler two things:

  1. You’re telling the compiler that you want to be able to use the “await” keyword inside the method (you can use the await keyword if and only if the method or lambda it’s in is marked as async). In doing so, you’re telling the compiler to compile the method using a state machine, such that the method will be able to suspend and then resume asynchronously at await points.
  2. You’re telling the compiler to “lift” the result of the method or any exceptions that may occur into the return type. For a method that returns Task or Task, this means that any returned value or exception that goes unhandled within the method is stored into the result task. For a method that returns void, this means that any exceptions are propagated to the caller’s context via whatever “SynchronizationContext” was current at the time of the method’s initial invocation.

Does using the “async” keyword on a method force all invocations of that method to be asynchronous?

No. When you invoke a method marked as “async”, it begins running synchronously on the curren thread. So, if you have a synchronous method that returns void and all you do to change it is mark it as “async”, invocations of that method will still run synchronously. This is true regardless of whether you leave the return type as “void” or change it to “Task”. Similarly, if you have a synchronous method that returns some TResult, and all you do is mark it as “async” and change the return type to be “Task”, invocations of that method will still run synchronously.

Marking a method as “async” does not affect whether the method runs to completion synchronously or asynchronously. Rather, it enables the method to be split into multiple pieces, some of which may run asynchronously, such that the method may complete asynchronously. The boundaries of these pieces can occur only where you explicitly code one using the “await” keyword, so if “await” isn’t used at all in a method’s code, there will only be one piece, and since that piece will start running synchronously, it (and the whole method with it) will complete synchronously.

For more see here:

http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/async-await-faq.aspx

Suren Srapyan
  • 66,568
  • 14
  • 114
  • 112
  • I just want to admit that it is not recommended to use "void async" method and use "async Task" instead. – Michal Kania Nov 16 '15 at 09:07
  • Not possible for event handlers @Michal. As in OPs example, this is the only way to comply to the event handler signature. – Patrick Hofman Nov 16 '15 at 09:09
  • 2
    @Hans - it *does* run asynchronously, *if* you use the async mechanisms that the `async` keyword makes available to you. That's not explicitly stated, but since `async` and `await` are always introduced in tandem, it's kind of assumed. – Damien_The_Unbeliever Nov 16 '15 at 09:11
  • Yes, you're right. I think we both did the same answers saying that you need to create your own asynchronous method to call it with await inside the event and what I mean exactly is that the method you create should be Task not a "void async". – Michal Kania Nov 16 '15 at 09:14
3

Async/await allow you to create code that can run asynchronously. It can even run in parallel (which is orthogonal to asynchrony). However, whether it does run parallel or not depends on how your task is scheduled.

When your code is called from a UI or ASP.NET context (more specifically, from the main thread in a context with UI control from these frameworks, since most controls can only be accessed on the thread that owns them), it is not scheduled to a background thread by default. You will still see that the execution of your code will wait for the Task to complete before continuing, but because the Task is scheduled to the same (main) thread, it will block any other actions on that thread (e.g. handling UI events).

If you know that your code is safe to execute in a background thread (again, this is usually because you know you are not accessing any control properties that are thread affine), you can overwrite the scheduling behavior with ConfigureAwait:

await DoWorkAsync().ConfigureAwait(false);

See also: Async/Await - Best Practices in Asynchronous Programming

Jimmy
  • 27,142
  • 5
  • 87
  • 100
1

Async is just a declaration that you might use asynchronous method. If you'll create a Task like

private async Task<int> callMe()
{
   int i;
   for(i = 0; i<int.max; i++)
      ;
   return i;
} 

you can run your code with line await callMe(); inside your KeyPressed event. Of course you don't have to return any value but it's just a practical example.

Michal Kania
  • 609
  • 11
  • 24
0

Just because you declare a method async doesnt make it automatically running in another thread.
You have to start one yourself with Task.Run().

So in your case this would be:

private async void Fomr1_KeyPressed(object sender, EventArgs e)
{
   await Task.Run(() => {
       for(int i=0; i<int.max; i++);
   });
}

Some good starting points for async/await:
Asynchronous Programming with Async and Await
Parallel Programming with .NET
Task Schedulers

Domysee
  • 12,718
  • 10
  • 53
  • 84
  • 2
    No, it is 'fire and forget'. No need to run a task. Then the `async` keyword would be useless anyway... – Patrick Hofman Nov 16 '15 at 08:53
  • 2
    If the async method you are calling does not start another thread, it will still run on the UI thread. The fire and forget is just because you cannot await the resulting Task – Domysee Nov 16 '15 at 08:55
  • @Domysee Ahaa, it's all about thread. Thanks. Would you reflect this to your answer to enable me accept it? – Hans Nov 16 '15 at 08:57
  • @Oliver Nice catch. Didn't know. But why Eric Lippert says another thing? – Hans Nov 16 '15 at 08:57
  • Just because you start a Task by calling `Task.Run()` doesnt make it automatically running in another thread also - it just enqueue it in the global queue, where theoretically, the same thread can pick it up. – shay__ Nov 16 '15 at 08:58
  • @shay__ Thank you. What is a global queue? Would you please tell me where I can find detail description of how windows threading and asynchrony works? – Hans Nov 16 '15 at 08:59
  • @Hans how would you like me to change the answer? I think I already did say that you have to start a thread – Domysee Nov 16 '15 at 09:02
  • @Domysee Yes, sorry. My mistake. I read you comment and thought it is not in answer – Hans Nov 16 '15 at 09:03
  • 1
    @Hans TPL and the async/await feature are well documented in MSDN, and this can be a good place to start at - https://msdn.microsoft.com/en-us/library/dd997402(v=vs.110).aspx. – shay__ Nov 16 '15 at 09:04
  • @shay__ thank you for the correction, I did not add that because it would complicate the answer and on most machines calling Task.Run is equivalent to executing the action on another thread. – Domysee Nov 16 '15 at 09:05
0

I found this explanation from Filip Ekberg very enlightening:

Now it's important to remember that everything after the await keyword is executed in a continuation. The difference between using await and ContinueWith is that the continuation is in fact on the caller thread which in this case is the UI thread.

So:

private async void Form1_KeyPressed(object sender, EventArgs e)
{
   // ----------------------> Surely, on UI thread
   await Something(); ------> (May be) on new thread
   // ----------------------> Surely, on UI thread
   for(int i=0; i<int.max; i++)
      ;
}
Hans
  • 2,674
  • 4
  • 25
  • 48