The async Keyword and Thrown Exceptions
async is a new keyword in C# 5.0 which allows us to easily author methods which execute asynchronously in regards to the calling code. The calling code can call the async method and continue its execution until it decides to join with the called method at a later stage.
The async Trap
Consider the following method:
public void Process(string s)
{
if (s == null) throw new ArgumentNullException("s");
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));
}
}
Depending on the length of the input string, this method could take a while to execute. Without using the async keyword we could implement this method to execute asynchronously using the Task-based asynchronous pattern:
public Task ProcessAsync1(string s)
{
if (s == null) throw new ArgumentNullException("s");
return Task.Run(() => ProcessCore(s));
}
However, as sophisticated developers, we would like to take advantage of the new async keyword and write some “cool” code that looks like this:
public async Task ProcessAsync2(string s)
{
if (s == null) throw new ArgumentNullException("s");
await Task.Run(() => ProcessCore(s));
}
Now let’s consider all three implementations. Semantically, all three implementations perform the same exact string processing (either in a synchronous or asynchronous manner), however there are some differences lurking underneath. What do you think the following code does?
var asyncTest = new AsyncClass();
asyncTest.Process(null);
Well, you must tell yourself, this call throws an ArgumentNullException and you are correct. Let’s take it up a notch – what does the following code do?
var asyncTest = new AsyncClass();
Task task = asyncTest.ProcessAsync1(null);
Hmmmmm… let’s think. The call to ProcessAsync1 checks the argument’s value, sees that it is invalid, and throws an ArgumentNullException! If that’s what you thought then you are correct again. But what does the following code do?
var asyncTest = new AsyncClass();
Task task = asyncTest.ProcessAsync2(null);
Isn’t it the same as the example before? Isn’t this the same like calling ProcessAsync1 with the same argument? Won’t this throw an ArgumentNullException? Unfortunately, this answer is wrong. Executing the above call will not throw any exception. The reason is that by marking ProcessAsync2 with the async keyword, we are actually saying that the code should be executed in an asynchronous manner. Even though the method code until the await keyword is actually called synchronously, the exception shall only be raised to the calling code when it awaits the task to complete. Unless the returned Task is waited upon, this exception shall be swallowed and never noticed by the application which is something you’d obviously like to lookout for.
Conclusion
The async & await keywords are awesome. They truly are. However, when using them one must fully understand the concepts behind these keywords and their expected behavior. await has some of its own quirks and pitfalls… I intend to blog about these in a following post.
Update: A similar post on the await keyword can be found here.
2 Comments