Do Using Statements and Await Keywords Play Nicely in C#

Do using statements and await keywords play nicely in c#

Yes, that should be fine.

In the first case, you're really saying:

  • Asynchronously wait until we can get the response
  • Use it and dispose of it immediately

In the second case, you're saying:

  • Asynchronously wait until we can get the response
  • Asynchronously wait until we've logged the response
  • Dispose of the response

A using statement in an async method is "odd" in that the Dispose call may execute in a different thread to the one which acquired the resource (depending on synchronization context etc) but it will still happen... assuming the thing you're waiting for ever shows up or fail, of course. (Just like you won't end up calling Dispose in non-async code if your using statement contains a call to a method which never returns.)

Benefits of using async and await keywords

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.

Using await on async method that returns only Task

During the execution, there were no unwanted consequences whatsoever.

I disagree. The resulting code is dangerous. ASP.NET pre-Core was able to detect similar situations and throw an exception ("An asynchronous module or handler completed while an asynchronous operation was still pending"). For technical reasons, ASP.NET Core cannot detect this situation so you don't get that "safety net" exception, but the situation itself is still just as bad.

The thing is, Visual Studio generates no warning message either when such thing happens

You don't get CS4014 ("Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the await operator to the result of the call.")?

is there actually any danger present of lefting out the await in such cases? I know that await should be applied on every async method naturally, but I don't really understand the reasons behind this when caller practically has no return value to use. Perhaps something with catching exceptions?

Yes, there are dangers. Task (even without a result type) is used for two things: for the caller to know when the operation completes, and for the caller to detect exceptions from that operation.

So, the one issue is that exceptions are silently swallowed. More specifically, exceptions from the async method are captured by the async state machine and placed on the returned Task, which is then ignored.

if I handle the exceptions in the said methods themselves (the ones not properly awaited), could we then say that all is fine and well?

No, because the other issue still exists: the caller doesn't know when the asynchronous operation completes. This is particularly important to know in ASP.NET, because the result should not be sent until the operation is complete. Any kind of "fire and forget" code on ASP.NET lives outside the request/response lifetime; i.e., it's request-extrinsic code.

I go into some detail on my blog about why request-extrinsic code is dangerous. In summary, your ASP.NET handler may complete too soon, and in that case, the request-extrinsic code may get "lost". At the very least, whatever it's doing won't be done by the time the response is sent; and in the case of a regular shutdown (e.g., rolling upgrades), it might not get done at all.

Why can't I use the 'await' operator within the body of a lock statement?

I assume this is either difficult or impossible for the compiler team to implement for some reason.

No, it is not at all difficult or impossible to implement -- the fact that you implemented it yourself is a testament to that fact. Rather, it is an incredibly bad idea and so we don't allow it, so as to protect you from making this mistake.

call to Monitor.Exit within ExitDisposable.Dispose seems to block indefinitely (most of the time) causing deadlocks as other threads attempt to acquire the lock. I suspect the unreliability of my work around and the reason await statements are not allowed in lock statement are somehow related.

Correct, you have discovered why we made it illegal. Awaiting inside a lock is a recipe for producing deadlocks.

I'm sure you can see why: arbitrary code runs between the time the await returns control to the caller and the method resumes. That arbitrary code could be taking out locks that produce lock ordering inversions, and therefore deadlocks.

Worse, the code could resume on another thread (in advanced scenarios; normally you pick up again on the thread that did the await, but not necessarily) in which case the unlock would be unlocking a lock on a different thread than the thread that took out the lock. Is that a good idea? No.

I note that it is also a "worst practice" to do a yield return inside a lock, for the same reason. It is legal to do so, but I wish we had made it illegal. We're not going to make the same mistake for "await".

Can we have an async Action?

My problem is that I randomly have silent crashes, and I now realize that this might be because the actions I call may (frequently are) async methods, and too late I am learning that exceptions in non awaited async methods are not caught anywhere.

Sort of. Exceptions thrown from normal asynchronous methods (i.e., ones that return Task or Task<T>) are just ignored if the task is never observed. Exceptions thrown from async void methods do crash the process; this is a deliberate design decision.

Is there a way to define an Action as async,

Sure; just map delegates to function signatures, make them asynchronous, and then map back. That will give you the asynchronous delegate types (as described on my blog).

In this case, Action maps to a method like void Handle(); the asynchronous version of which is Task HandleAsync(), which maps to a delegate of type Func<Task>.

so you can await it when later calling it,

Welllll, sort of. Sure, await handler.Invoke(); will compile, but events (and delegates) can hold any number of handlers, and Invoke just returns the last result, so the await awaits the Task returned from the last handler. If there are multiple handlers, all other Tasks are discarded (and their exceptions silently swallowed).

or might there be a smarter solution to this issue?

There's a few solutions for asynchronous events (link to my blog).

First, I'd consider adjusting the design. Events are a good match for the Observer design pattern, but the need to await handlers usually means events are being misused to implement a different design pattern, usually the Command or Strategy design patterns. Consider replacing the event completely with something different, like an interface.

If that isn't feasible, then I'd consider using a different event representation by investigating Reactive Observables. Observables are really what events should be. But observables can get pretty complex.

Another approach is to use deferrals. Essentially, you keep the async void method but the handlers need to "enlist" in asynchronous handling by acquiring a deferral in a using. The invoking code then waits for all deferrals to be released.

The final approach is to use Func<Task>, get all the tasks via GetInvocationList, and await them one at a time or with Task.WhenAll.

Await for the user to click on one control within multiple controls

In general these kinds of problems are solved by creating a statemachine, I.e. something that know what state the game is in, and depending on this state, how to handle the next input. There are several ways to program state machines, but one way is to use async/await to let the compiler do it.

You can wrap a button in a class that completes a task each time the button is pressed:

public class ButtonAwaiter
{
private readonly Button button;
private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
public ButtonAwaiter(Button button)
{
this.button = button;
this.button.Click += OnClick;
}

private void OnClick(object sender, EventArgs e)
{
tcs.SetResult(true);
tcs = new TaskCompletionSource<bool>();
}

public Task GetTask() => tcs.Task;
}

Substitute 'bool' for whatever the button represents.

And await when the first of the buttons is pressed like:

    public async Task DoGameLoop()
{
var b1 = new ButtonAwaiter(button1);
var b2 = new ButtonAwaiter(button2);
while (GameIsInProgress)
{
var pressed = await Task.WhenAny(new[] {b1.GetTask(), b2.GetTask()});
}
}

This method is especially nice when the user needs to follow some sequence of actions since it lets you write the code in a reasonably straightforward manner.

Does Task.Result have any impact when used after the await statement?

Would Task.Result cause any issues [if the task is already completed]?

Task<T>.Result will synchronously wait for the task to complete (the same as task.Wait()), and then return the result (the same as await task).

Since your task is already completed, there is only one other difference between Result and await that is important: Result will wrap exceptions in an AggregateException. For this reason (and also to make the code more refactor-safe), I use await instead of Result whenever possible. That is, take the DoSomethingAsync2 approach.

I've read a bit about some blocking and deadlock issues that Task.Result can cause, but I think it wouldn't in this case, since it runs on a separate task and there is an await on the task which already ensures the Result.

It wouldn't because it's running in the thread pool context.

So, this is something I'm planning to do in a step-by-step way, starting with operations that doesn't depend on main thread.

You may find my brownfield async article helpful. In that article, I refer to this technique as "The Thread Pool Hack".

Are there any hidden bottlenecks/issues that I need to be aware of in the below scenario?

A few:

You shouldn't use StartNew. Use Task.Run instead.

You shouldn't fire-and-forget on ASP.NET (i.e., call an asynchronous method without consuming its task).

You shouldn't expose fake-asynchronous methods (i.e., methods with an asynchronous signature but that are implemented by blocking a thread pool thread). Instead of using Task.Run to implement a method, use Task.Run to call a method.

Combining those three points, the proper structure of The Thread Pool Hack is to use Task.Run with GetAwaiter().GetResult() (which is just like Result but avoids the AggregateException wrapper):

public ActionResult Index()
{
var r = Task.Run(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong())
.GetAwaiter().GetResult();
return View();
}

As a final issue, as long as you have this pattern in your ASP.NET application, the scalability of your application will be decreased. Instead of using one thread during DoSomethingThatTakesReallyLong, your app is now using two; this may also throw off the ASP.NET thread pool heuristics. This may be acceptable during the transition to async, but keep it in mind as a motivator to finish the transition.

Is there any downside when adding 'async' keyword to functions?

Lets look at the MSDN definition of async modifier:

Use the async modifier to specify that a method, lambda expression, or anonymous method is asynchronous. If you use this modifier on a method or expression, it's referred to as an async method.

And they continue:

The method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method, as the example in the next section shows.

If the method that the async keyword modifies doesn't contain an await expression or statement, the method executes synchronously. A compiler warning alerts you to any async methods that don't contain await, because that situation might indicate an error.

The async keyword comes into play when combined with an await. It is a sign telling the compiler "this method should compile as a state-machine, so the control can yield back to the caller and continue when the asynchronous operation is complete and resume the rest of the code as a continuation".

If no await keyword is present in the method, it will simply run synchronously from beginning to end, but the code for the state will be emitted and that is an important factor.

Now, when an async method is awaited, there is a state machine that the compiler generates. This does have a cost, although the framework team made sure it has minimum effect on your codes performance. More so, behind the scenes there is the SynchronizationContext which flows when you await (unless explicitly stating not to) which is responsible for marshaling the continuation on back to the originating thread.

For more on the insides, especially performance-wise, see Async Performance: Understanding the Costs of Async and Await

Do async methods throw exceptions on call or on await?

Both are possible. If the method is actually async (i.e. uses the C# async keyword in the declaration), then the C# compiler wraps it up in such a way that it will always reliably throw on the await, but it is important to note that this is not the only way to write a method that can be await-ed, so: if you don't control the method being called (ThisMethodWillThrow) and can't rely on knowledge of the implementation, it would be better for the try to include the initial invoke, as well as the await.

As an example of a method that will throw immediately rather than in the await:

Task ThisMethodWillThrow() { // note that this is **not** "async", but is awaitable
if (thingsAreBad) throw new SomeException();
return SomeInnerMethod();
}
async Task SomeInnerMethod() { ... }

It might be tempting to think "well, just make all awaitable methods async, to avoid this" - like:

async Task ThisMethodWillThrowToo() { // note that this is "async"
if (thingsAreBad) throw new SomeException();
await SomeInnerMethod();
}

However: there are scenarios where the async machinery is a very measurable performance overhead in the "often sync, sometimes async" case - and so a common optimization in performance critical awaitable code (IO/network code, for example) is to actively avoid the async machinery unless we know that we're actually falling into the asynchronous path.



Related Topics



Leave a reply



Submit