Task.Wait() vs. await
The await keyword is a new keyword in C# 5.0 which, in tandem with async keyword, allows us to easily author methods which execute asynchronously in regards to the calling code. In a previous post I’ve shown a certain issue you should look out for when using the async keyword. In this post we’ll check a similar issue with the await keyword.
The await Trap
Let’s remember our test code from the previous post:
class AsyncClass
{
public void Process(string s)
{
if (s == null) throw new ArgumentNullException("s");
ProcessCore(s);
}
public Task ProcessAsync1(string s)
{
if (s == null) throw new ArgumentNullException("s");
return Task.Run(() => ProcessCore(s));
}
public async Task ProcessAsync2(string s)
{
if (s == null) throw new ArgumentNullException("s");
await Task.Run(() => ProcessCore(s));
}
private void ProcessCore(string s)
{
for (int len = 1; len <= s.Length; len++)
{
Thread.Sleep(1000); // This task is very complicated!!
Console.WriteLine(s.Substring(0, len));
}
}
}
What do you think the following code does?
var asyncTest = new AsyncClass();
await asyncTest.ProcessAsync2(null);
Well, you’re probably saying that this time we are actually awaiting the method to complete so it must throw an ArgumentNullException! And, you’re correct – this is indeed the result of the above code. But how about the following code?
var asyncTest = new AsyncClass();
asyncTest.ProcessAsync2(null).GetAwaiter().GetResult();
This method performs a wait on the returned task until it completes, it does not wrap the returned exception in an AggregateException and it does not require the await keyword which is limited only to methods marked with async.
Conclusion
Sample code demonstrating this behavior can be found here (VS2012). The following code snippet summarizes the different consequences of calling our test methods:
asyncTest.Process(null); // ArgumentNullException
asyncTest.ProcessAsync1(null); // ArgumentNullException
asyncTest.ProcessAsync1(null).Wait(); // ArgumentNullException
asyncTest.ProcessAsync1(null).GetAwaiter().GetResult(); // ArgumentNullException
asyncTest.ProcessAsync2(null); // No Exception!
asyncTest.ProcessAsync2(null).Wait(); // AggregateException
asyncTest.ProcessAsync2(null).GetAwaiter().GetResult(); // ArgumentNullException
await asyncTest.ProcessAsync1(null); // ArgumentNullException
await asyncTest.ProcessAsync2(null); // ArgumentNullException
Task.Wait() and await are not exactly the same. You should be aware of their differences. Were you aware of them?
Hi,
“await asyncTest.ProcessAsync2(null);” will not throw an exception unless you add Thread.Sleep(200);
The reason is that the program is finished execution before the async operion raise an exception
Hi Boaz,
Actually no. It will throw the exception without any need for “Thread.Sleep()” since “await” itself does the wait. I suspect that the issue you are seeing is that you are not waiting for the asynchronous method encapsulating the “await” itself to finish, thus causing the program to terminate before it reaches this line.
As a side note, please be aware that using “Thread.Sleep()” for synchronization is not a good practice. It would have been better to do something like this:
static void Main()
{
Do().GetAwaiter().GetResult();
}
static async Task Do()
{
var asyncTest = new AsyncClass();
await asyncTest.ProcessAsync2(null);
}
HI,
You wrote:
“…and it does require the await keyword which is limited only to methods marked with async…”
Surely it should be:
“…and it does not require the await keyword which is limited only to methods marked with async….”
Yes, you are absolutely correct. Just fixed it.
Thank you!