Confusion regarding VSTHRD100 and VSTHRD110 for event handlers #1425
Replies: 2 comments 1 reply
-
I'm equally confused about these async patterns - some clarification would be great. The documentation feels like it's written from different perspectives that never got reconciled:
What would really help is a clear decision tree from the team:
Even if there's no perfect solution, understanding the tradeoffs would help us make consistent decisions in our codebases rather than oscillating between patterns based on which analyzer warning we're trying to fix that day. |
Beta Was this translation helpful? Give feedback.
-
That's a policy decision you can make.
That depends on whether you use What concerns you about
Because JTF.Run synchronously blocks the calling thread for your async work. That will cause UI delays in your application. And it forces the party raising the event to wait for your handler to do arbitrary work that it presumably does not directly care about.
The question of whether it's ok to use async void for event handlers is a contentious one. I see no reason personally to make an exception (no pun intended) for them. Either you want to track work and handle exceptions or you don't. Why should event handlers uniquely crash an application when most async methods (the ones that return
These are policy decisions that you will have to make yourself (or with your team). The analyzers are there to support certain policies, but if they don't match the policies you want to adopt, by all means you can disable the ones that conflict with your goals.
In a new WPF app, I would personally strive hard to avoid sync-blocking wherever I could. The hardest point might be on shutdown where you need to save files before exiting and that may be implemented asynchronously. But even then, I've pulled tricks like hiding the main window and allowing the main thread to continue until saving is done in order to avoid using JTF at all. |
Beta Was this translation helpful? Give feedback.
-
VSTHRD100 Avoid async void methods suggests that
async void
should be avoided, and to use the following pattern instead:Note the use of the discard for the
JoinableTask
returned from theRunAsync
method. If the discard is removed, it produces VSTHRD110 Observe result of async calls, which makes sense. What makes it "okay" to use a discard here to silence VSTHRD110?Either way (discard or no discard), doesn't this mean that any exceptions will go unobserved, and will (eventually) result in a
UnobservedTaskException
at some indeterminate time in the future whenever a garbage collection occurs?In the above scenario, when
ThrowExceptionAsync
throws an exception, it will go unobserved. That seems problematic. Is this "less bad" than usingasync void
where the exception is immediately observed, at the expense of crashing the application if not handled I guess?Why does VSTHRD100 Avoid async void methods suggest using
JoinableTaskFactory.RunAsync
rather thanJoinableTaskFactory.Run
? The latter seems like a more natural fit for a non-async
method like an event handler, with the caveat that we've found we've sometimes run into deadlock situations using it with more complex code.Also, VSTHRD100 Avoid async void methods suggests referring to Async/Await - Best Practices in Asynchronous Programming by Stephen Cleary which seems to directly contradict VSTHRD100, in that event handlers are an exception to the rule of "avoid async void", and that you should use
async void
for them (generally). This is confusing.I guess I'm fumbling around somewhat and trying to develop an understanding of what the best practices are so that I can communicate them to my team, and I'm finding I'm chasing my tail a bit with some of the analyzers, and the documentation is in some cases confusing/contradictory.
In another discussion it was mentioned that for new WPF apps,
JoinableTaskFactory
shouldn't be needed because you should beasync
from the start. While a worthy goal, I'm not sure how you fully achieve that when the language and technology push/force you into anti-patterns, e.g., event handlers that require avoid
signature, WPF requiring updates to happen on the main UI thread, etc.Any advice would be appreciated, thanks.
In case it helps, I've included a small, crude WPF app using .NET 8 that demonstrates various scenarios for event handlers and exception handling, some of which are discussed above. Note that you'll have to comment out all scenarios but one to run it.
MainWindow.xaml
MainWindow.xaml.cs
Beta Was this translation helpful? Give feedback.
All reactions