Confusion regarding VSTHRD100 and VSTHRD110 for event handlers #1425
prlcutting
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
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