I have a problem how to await async methods in WPF life-cycle methods (with Caliburn-Micro framework) (eg. OnActivate, OnInitialized, OnExit - which is bound directly to Application.Exit event)
This article exactly describes my problem: http://mark.mymonster.nl/2013/07/10/donrsquot-make-your-application-lifetime-events-async-void (now I am thinking of using the solution from this article, but seems like a bit overkill for the first look)
I need to await some async methods in my OnExit hanlder so I have it as async. And it works. Kind of. I do not understand why??, but on calling Application.Exit event it somehow waits until the method is completed, even if the handler is async void. Can you explain please how this is possible? And is this safe? Or is it just coicidence? Async void should be used only for Top-Level events, is this that case?
I looked in the code of System. And the binding looks like this:
public event EventHandler Exit
{
add
{
XcpImports.CheckThread();
this.AddEventListener(DependencyProperty.RegisterCoreProperty(20053U, (Type) null), (Delegate) value);
}
remove
{
XcpImports.CheckThread();
this.RemoveEventListener(DependencyProperty.RegisterCoreProperty(20053U, (Type) null), (Delegate) value);
}
}
which is really cryptic and I cannot see what really happens in .net framework by calling this event.
What is as well strange, that calling await Task.Delay(1) in the handler causes DeadLock when I do not use ConfigureAwait(false). So I would say there is somewhere .Wait() used deep in .net code.
Note: when I make OnActivate, OnInitialized handlers async, as expected, page is not waiting till handler completes.
Thx for your answeres!
Async void methods can wreak havoc if the caller isn't expecting them to be async. When the return type is Task, the caller knows it's dealing with a future operation; when the return type is void, the caller might assume the method is complete by the time it returns.
In short, if your async method is an event handler or a callback, it's ok to return void .
So async all the way up enables you to actually make an asynchronous call and release any threads. If it isn't async all the way then some thread is being blocked. So unless you await all the way up, there's no benefit.
If a method has no async operations inside it there's no benefit in making it async . You should only have async methods where you have an async operation (I/O, DB, etc.). If your application has a lot of these I/O methods and they spread throughout your code base, that's not a bad thing.
It is theoretically possible for a framework to detect the use of async void
and wait until the async void
method returns. I describe the details in my article on SynchronizationContext
. AFAIK, ASP.NET is the only built-in framework that will wait on async void
handlers.
WPF does not have any special treatment for async void
methods. So the fact that your exit handler is completing is just coincidence. I suspect that the operations you await
are either already complete or extremely fast, which allows your handler to complete synchronously.
That said, I do not recommend the solution in the article you referenced. Instead, handle the window's Closing
event, kick off whatever asynchronous saving you need to do, and cancel the close command (and also consider hiding the window immediately). When the asynchronous operation is complete, then close the window again (and allow it to close this time). I use this pattern for doing asynchronous window-level "close" animations.
I'm unable to repro the deadlock you describe. I created a new .NET 4.5 WPF application and added an exit handler as such:
private async void Application_Exit(object sender, ExitEventArgs e)
{
await Task.Delay(1);
}
but did not observe a deadlock. In fact, even with using Task.Yield
, nothing after the await
is ever executed, which is what I would expect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With