.NET Development Addict
source link: https://dotnetdevaddict.co.za/2021/11/11/exceptional-tasks/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
.NET Development Addict
Home is where the [.net] compiler is.
I am just putting this in a blog for now so I have it somewhere. There probably is a way better blog out there, but I need to also write a blog post so I never forget again.
So… What are we talking about today? Pretty simple thing really: how to move a method to a background thread, and still catch exceptions.
This example is very simple, so assume we have this method:
string
GetName()
{
try
{
return
ExpensiveNameGetter();
}
catch
{
return
null
;
}
}
Let’s ignore the fact that this should not be the case and pretend we have no other method we can call. The issue is, the expensive method is… well… expensive. So, our goal is to move this off the main/UI thread and onto some background thread pool. We have heard of Task
, so let’s use that!
async
Task<
string
> GetName()
{
try
{
return
await
Task.Run(() => {
return
ExpensiveNameGetter(); });
}
catch
{
return
null
;
}
}
Yay! Our code now runs in the background thread and all we had to do was make it a Task
and then we are done! Well, this is where another thing comes in… the async
and await
keywords. These keywords are all syntactic sugar to generate a fair bit of code. This is great because we don’t have to worry about how it all works, but it is also unnecessary.
You can see all the code it generates using this sharplap.io link.
One way we can solve this is to remove the async
/await
and return tasks and results (using Task.FromResult
):
Task<
string
> GetName()
{
try
{
return
Task.Run(() => {
return
ExpensiveNameGetter(); });
}
catch
{
return
Task.FromResult<
string
>(
null
);
}
}
But wait! This is all lies! Let’s look at the code again. What is happening in the try block? We are running a task you might say, and that is correct… -ish… Are you really running the task, or are you starting the task? If you look at the block before, we awaited the result of the task, but now we are not.
So what is really happening here? Exactly what you told it to do: “start this task, and if there are any exceptions, return null”. But, is this what we want? No. This is because there is a subtle difference in the desire of “run and await this task, and if there are any exceptions, return null”.
The original block wasawaiting the task to complete and would handle any exceptions during the run. This new block is just starting the task and handling any exceptions that occurred during the starting of that task!
This may sound like a bug or a missing feature, but in fact it is desired. What we are saying is that we want to execute some operation and return immediately, leaving the operation to continue – on a background thread/task. If we were to catch the exceptions that occur in the task, then we would have to block this method and wait for it to complete to be sure… and then we end up right where we started.
So… how do we handle this? How can we use background tasks and also catch exceptions – but not require a state machine? Very simple: put the try
/catch
inside the Task.Run
:
Task<
string
> GetName()
{
return
Task.Run(() =>
{
try
{
return
ExpensiveNameGetter();
}
catch
{
return
null
;
}
});
}
That’s it! Just a quick note to myself and maybe a reminder to anyone reading this to either make sure you are awaiting your tasks or catching the right exceptions.
In most cases, you should await, but in some cases all you do are running some single operation on a background thread. No need to spin up a state machine just for that!
Recommend
-
13
Performance in .NET – Part 1 Updated: thanks, Paulo Morgado! Updated: see the second post here and the thi...
-
7
.NET Development on Apple Silicon
-
1
programming in thetwenty-first centuryIt's not about technology for its own sake. It's about being able to implement your ideas.Tales of a Former Disassembly Addict
-
7
Beware of C# 8 Using Statements – .NET Development Addict Let’s be honest here, C# 8 is crazy cool. It has such a big set of new features to make developers more productive and write even better code. There are some awesome things, li...
-
0
.NET Development Addict Home is where the [.net] compiler is. Recently, I have been doing more and more work with PowerShell, which...
-
9
.NET Development Addict Home is where the [.net] compiler is. Xamarin.Forms is really cool! It allows you to almost write y...
-
6
Turning Events into Commands – .NET Development Addict Have you ever used some control in Xamarin.Forms that appears to have an event instead of a command? You are working the MVVM love and then you come across that annoying control....
-
7
Contents The Control – creating the templated control
-
2
Who cares about the view anyway? – .NET Development Addict While working on an issue, I discovered a cool way in which to draw using SkiaSharp –...
-
7
"Game of Thrones: Conquest" and "State of Survival" players like Charissa Keebaugh (left) and Paul Mazey (right) felt pressure to continue paying to play or risk losing their friends and community. Photos courtesy of Keebaugh and Mazey.
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK