The beginning of an async method is executed just like any other method. That is, it runs synchronously until it hits an "await" or throws an exception. Await is where things can get asynchronous. Await is like unary operator, it takes a single argument, an awaitable. Examines that awaitable to see if it has already completed; then the method just continues running, synchronously just like regular method. But if awaitables are not completed yet, it waits for awaitable to complete or throw exception continue remaining of program flow.
When method returns awaitables: Task or Task or void(don't recommend this, explained below), its saying you can await the result of such methods. Its not because method has async but it returns Task, which means you can await for the result of non-async method that returns Task. But you can't use await within such method.
public async Task Method1(){
//you can use await here.
await...
}
public Task Method2{
//you can't use await here.
}
So, what is the benefit of using non-async method that returns a Task? Well, in my opinion, its just good idea to not use aync method when you don't have to. async is needed only when you need to await. If your method is just an intermediary which is just calling another async method, then just return the Task.
public Task<int> NonAsyncMethod(){
var someService = new SomeService();
return someService.CallAync();
}
To support further, a tip from Stephen Cleary; if you have a very simple asynchronous method, you may be able to write it without using the await keyword. If you can write it without await, then you should write it without await, and remove the async keyword from the method. A non-async method returning Task.FromResult is more efficient than an async method returning a value. Also note that you can't return Task from non-async method. If an async method returns Task or void, there is no return value however if such method return Task, the method needs to return the type of T.
public Task<int> NonAsyncMethod(){
.
.
return 5; //compile error, you need async method to do this.
}
public async Task<int> Method1(){
await ...
.
.
return 2; // need to return integer
}
Alright, after knowing these basics, working on your first async/await codebase, you may ended up with two cases especially with ASP.NET as follow, else everything has gone well with you, congrats:
- Everything is working, results are as expected. However it seems every tasks are running synchronously.
- Seems like every tasks are completed but await never seems knowing if tasks are completed.
The root case for first senario is either you don't have await in your async method. Without await, async method execute the method as the normal method execution. Or somewhere down the pipe, you are making a blocking call. or you have explicitly called task to run synchronously.
The second scenario is a deadlock situation, which you may not like. This usually happens with UI or ASP.NET context when, Main calling method is waiting for called async method, which in turn waiting for the main context to excute the remaining of its method. Please check my example repo showing such scenario.
So, to prevent the deadlock:
- Make all the way Async(see my repo).
- use configurAwait(false) where possible(see my repo demonastrated with ASP.NET)
This bring us to some best practices using async and await as mentioned by Stephen Cleary.
- Avoid Async void Especially because of the way the exceptions are handled when using Async void. When exception is thrown, it will be raise directly on the SynchronizationContext that was active when the async void method started. In the other hand, with async method with return type Task and Task
, exceptions are captured and stored on the Return Task itself, resulting easier and simpler handling. Void returning async methods have a specific purpose: to make asynchronous event handlers possible. - Async all the way Means you shouldn't mix synchronous and asynchronous code. Check out following ASP.NET code, async starts from controller and continues with async calls if needed. The promise is you don't have to use wait that blocks the calling method.
public class MyController:APiController { public async Task<string> Get(){ var result = await callAnotherAsync(...); return result.ToString(); } }
However, things might not be as simple as said, making all asynchronous. There are always some limitation, may be the framework you are using doesn't support async or may be you have a giant codebase and you just want to make certain section to be asynchronous. But you need to be careful and know the consequences. This is where you might end up with deadlock. Checkout out my [repo] that demonstrates the deadlock code. - Use configureAwait(false): When using configureAwait(false), you are running remaining of the method into separate thread instead of original calling Thread. This is very important to use when possible as this could also increase efficiency as well as stop from deadlock. However you need to be careful as you can't always use configure(false), like if the calling thread is UI-Thread.
That's all about async and await. The concept is very close to promise pattern in javascript. Its very simple compare to concurrent programming using threads. I really liked this feature of c#.
ref
- https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
- https://www.youtube.com/watch?v=MCW_eJA2FeY
- http://blog.ciber.no/2014/05/19/using-task-configureawaitfalse-to-prevent-deadlocks-in-async-code/
- http://blog.stephencleary.com/2012/02/async-and-await.html
- http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
No comments:
Post a Comment