Imagine a WPF code-behind event handler:
<Button Click="OnButtonClick" />
In C# 4 you would declare your handler as:
private void OnButtonClick(object sender, RoutedEventArgs e) { ... }
In C# 5 you can declare an async handler
private async void OnButtonClick(object sender, RoutedEventArgs e) { ... }
So what is WPF doing with this? A few minutes of searching about didn't turn anything up.
It seems that it's possible to perform UI updates after await statements. Does this imply that the task is continued on the Dispatcher thread?
If the Task raised an error, would it be raised through the WPF Dispatcher, or only via the TaskScheduler?
Are there any other interesting aspects to this that might be nice to understand?
You may find my async/await intro helpful.
An async method is re-written by the compiler to support the await operator. Every async method starts out synchronous (in this case, on the UI thread) until it awaits some operation (that is not already completed).
By default, the context is saved, and when the operation completes, the rest of the method is scheduled to execute in that context. The "context" here is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current. As Drew pointed out, WPF provides a DispatcherSynchronizationContext which is tied to the WPF Dispatcher.
Regarding error handling:
When you await a Task inside a WPF async void event handler, the error handling goes like this:
The Task completes with an error. The exception is wrapped into an AggregateException, like all Task errors.
The await operator sees that the Task completed with an error. It unwraps the original exception and re-throws it, preserving the original stack trace.
The async void method builder catches the exception escaping from an async void method and passes it to the SynchronizationContext that was active when the async void method started executing (in this case, the same WPF context).
The exception is raised (with the original stack trace, and without any annoying AggregateException wrapping) on the Dispatcher.
This is rather convoluted, but the intent is to have exceptions raised from async event handlers be practically the same as exceptions raised from regular event handlers.
A partial answer. From MSDN:
An async method that has a void return type can’t be awaited, and the caller of a void-returning method can't catch any exceptions that the method throws.
So any errors would only be available via the TaskScheduler.
Also, there's nothing XAML-specific going on with the event handler registration. It could have been done in code:
this.button.Click += OnButtonClick;
Or even as an async lambda:
this.button.Click += async (s,e) => { ... };
As for safety of UI updates after an await, it seems that the continuation is executed within SynchronisationContext.Current, which is set per thread. In WPF, this is a DispatcherSynchronisationContext that's coupled to the WPF Dispatcher that pumped the event in the first place.
Related
Given the following code:
public delegate Task AsyncEventHandler<in TEventArgs>(object sender, TEventArgs eventArgs);
public static Task InvokeAsync(this AsyncEventHandler eventHandler, object sender, EventArgs eventArgs)
{
if (eventHandler == null) return Task.CompletedTask;
var delegates = eventHandler.GetInvocationList().Cast<AsyncEventHandler>();
var tasks = delegates.Select(it => it.Invoke(sender, eventArgs));
return Task.WhenAll(tasks);
}
I have a test function whereby the faulty handlers should throw exceptions, and the workinghanlder should run - currently only the first FaultyHandler1 is called and no others event handlers.
private class NonGenericNotifier
{
public event AsyncEventHandler SomethingHappened;
public Task OnSomethingHappening() => SomethingHappened.InvokeAsync(this, EventArgs.Empty);
}
public async Task Multiple_Exceptions_That_Occur_During_Event_Handling_Should_Be_Propagated()
{
var isHandler1Called = false;
var isHandler2Called = false;
var isWorkingHandlerCalled = false;
var notifier = new NonGenericNotifier();
Task FaultyHandler1(object sender, EventArgs eventArgs)
{
isHandler1Called = true;
throw new InvalidOperationException();
}
Task FaultyHandler2(object sender, EventArgs eventArgs)
{
isHandler2Called = true;
throw new InvalidOperationException();
}
Task WorkingHandler(object sender, EventArgs eventArgs)
{
isWorkingHandlerCalled = true;
return Task.CompletedTask;
}
notifier.SomethingHappened += FaultyHandler1;
notifier.SomethingHappened += FaultyHandler2;
notifier.SomethingHappened += WorkingHandler;
await Should.ThrowAsync<InvalidOperationException>(async () => await notifier.OnSomethingHappening());
isHandler1Called.ShouldBe(true);
isHandler2Called.ShouldBe(true);
isWorkingHandlerCalled.ShouldBe(true);
}
Assuming a single exception can be thrown I beleive this should be an AggregateException containing an exception for each Task, and most importantly the InvokeAsync method above should bail on the first exception encountered.
I have started to create a List<Exception> within the InvokeAsync extension method, and wrap each it => it.Invoke(sender, eventArgs) with a try/catch construct and within the catch add the exception to the exception list.
However I am lost on how to collate this list of exceptions and then send on as an AggregateException.
UPDATE (FIX?)
Thanks to Artur for pointing me in the right direction. I changed the InvokeAsync extension method to the below, and it works - no longer halting on the first task. We have gone from var tasks = delegates.Select(it => it.Invoke(sender, eventArgs)); to the below using the code here:
public static Task InvokeAsync(this AsyncEventHandler eventHandler, object sender, EventArgs eventArgs)
{
if (eventHandler == null) return Task.CompletedTask;
var delegates = eventHandler.GetInvocationList().Cast<AsyncEventHandler>();
var tasks = delegates.Select(async it => await it.Invoke(sender, eventArgs));
return Task.WhenAll(tasks).WithAggregatedExceptions();
}
static Task WithAggregatedExceptions(this Task task)
{
// using AggregateException.Flatten as a bonus
return task.ContinueWith(
continuationFunction: priorTask =>
priorTask.IsFaulted &&
priorTask.Exception is AggregateException ex && (ex.InnerExceptions.Count > 1 || ex.InnerException is AggregateException) ? Task.FromException(ex.Flatten()) : priorTask,
cancellationToken: CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
scheduler: TaskScheduler.Default).Unwrap();
}
My issue is subscribers of this event, writing synchronous handlers over which I have no control - this would stop the other event handlers (sync and async) running that are attached to the same event.
I also appreciate this is the designed function of Task.WhenAll if you were mixing async and non-async handlers... if there is one reason to not write synchronous code in an async function without await Task.Yield() this is it.
Question
Can we say that wrapping the delegates.Select(async it => await it.Invoke(sender, eventArgs) with async/await allows synchronous method to run, and at worst(?) wrap twice an async method (which is the same as nesting async/await function calls) so is actually a non-issue?
Are there any side effects that have been introduced?
With the bounty looking for authorative guidance on how this would be implemented, one answer (much appreciated for contributing to the discussion) says to avoid async events, yet in other places like the discord c# client they have embraced async events (with timeout wrappers etc).
Task.WhenAll will invoke all the handlers when it reifies its parameter. It will invoke them one at a time, and then will asynchronously wait for all the tasks to complete.
The reason you were seeing the halting on the first exception was because the exception was thrown during reification. It's normal for asynchronous (Task-returning) functions to place any exceptions on the returned task. It's abnormal for asynchronous functions to throw exceptions directly.
So, this is the problematic code:
Task FaultyHandler1(object sender, EventArgs eventArgs)
{
isHandler1Called = true;
throw new InvalidOperationException();
}
One of these would be correct:
async Task FaultyHandler1(object sender, EventArgs eventArgs)
{
isHandler1Called = true;
throw new InvalidOperationException();
}
Task FaultyHandler1(object sender, EventArgs eventArgs)
{
isHandler1Called = true;
return Task.FromException(new InvalidOperationException());
}
You're seeing the odd behavior because the asynchronous handler is misbehaving (by throwing a synchronous exception).
Now, if you want to allow misbehaving asynchronous handlers, you can do that, either with an explicit try/catch or the extra async/await:
var tasks = delegates.Select(it => try { return it.Invoke(sender, eventArgs); } catch (Exception ex) { return Task.FromException(ex); });
// async/await is necessary to handle misbehaving asynchronous handlers that throw synchronous exceptions
var tasks = delegates.Select(async it => await it.Invoke(sender, eventArgs));
If you do keep the async/await approach, please do comment it, because coding constructs like that are often assumed to be spurious and may be removed by a future maintainer.
The WithAggregatedExceptions looks fine as-is, but it can be simplified if you want:
static async Task WithAggregatedExceptions(this Task task)
{
try { await task; }
catch { throw task.Exception; }
}
My issue is subscribers of this event, writing synchronous handlers over which I have no control - this would stop the other event handlers (sync and async) running that are attached to the same event.
Well, yes. Task.WhenAll reifies its collection of tasks synchronously, which invokes all the handlers one at a time.
If you want to allow synchronous handlers as well as asynchronous ones all at the same time, you can wrap the invocations in a Task.Run:
var tasks = delegates.Select(it => Task.Run(() => it.Invoke(sender, eventArgs)));
Can we say that wrapping the delegates.Select(async it => await it.Invoke(sender, eventArgs) with async/await allows synchronous method to run, and at worst(?) wrap twice an async method (which is the same as nesting async/await function calls) so is actually a non-issue?
The extra async/await for asynchronous handlers is a non-issue; it's very slightly less efficient, and appears unnecessary, so I'd say it's in danger of being removed (unless commented). It doesn't "allow synchronous methods to run"; instead, it corrects the misbehaving methods that throw exceptions directly instead of placing them on the returned Task as expected.
Are there any side effects that have been introduced?
Not really. If you do use the Task.Run approach, then all the handlers are invoked on thread pool threads and may run concurrently, which may be surprising.
one answer (much appreciated for contributing to the discussion) says to avoid async events, yet in other places like the discord c# client they have embraced async events (with timeout wrappers etc).
I am 100% in agreement with that answer.
Here's how I think about it:
The Observer pattern is a way to notify observers of state changes. The Observer pattern is a clear fit for "events" in OOP: any number of observers may subscribe to state change notifications. This is what C# events were patterned after: notifying subscribers of things. There's no mechanism for information to "flow back". While the language allows C# events with return values, it's not natural by any means. The same limitation happens with exceptions (which can be considered a kind of return): the standard handler?.Invoke pattern starts to break (stopping invocations at the first exception, etc).
As soon as you have information "flowing back" (including needing to handle exceptions, or needing to await all the handlers to complete), you're no longer in the Observer pattern, and you are no longer in the happy path of C# events.
Generally, I find most "events" of this type are usually related to a Strategy or Visitor pattern, rather than Observer. Neither Strategy nor Visitor are good fits for C# events, although they are often (sadly) implemented that way. I consider that a common design mistake (for all OOP languages).
In my opinion, the design of using C# events in an async way is not robust, and it will always behave in a slightly uncontrolled way. There are better techniques to make event processing robust.
One of the best such technologies is TPL Dataflows (https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library). This library allows you to program stream processing in a very controlled way, it helps you deal with task schedulers etc. Once you apply this successfully, all the problems in your question will be addressed.
There are obviously other alternatives out there, but I would clearly abstain from re-implementing this by using C# events....
On a button_click event, I start a Task to do some time consuming calculations asynchronously. I use Task.ContinueWith and set the TaskSheduler for the continuation to the UI synchronization context to display the result of the asynchronous calculations in a Textbox.
Now lets say this last part throws an Exception like this:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(DoSomeCalculations).ContinueWith
(t => { throw new Exception("Some Exception"); }, TaskScheduler.FromCurrentSynchronizationContext());
}
If I enable the debug option "Enable Just My Code" then the program halts and I get a warning: "Exception was unhandled by user code"
But if I don't set this option, the Exception disappears in nowhere (no program crash, just nothing).
So how/where can I handle this Exception?
Edit: Please note, as the tag suggests, I'm using .NET-4.0
If you want to handle the exception then either have a try/catch block inside the continuation itself, so that it handles its own exceptions or add an additional continuation to the continuation to handle exceptions.
Note that if you use await rather than ContinueWith to add continuations it's typically simpler, particularly when dealing with exceptions, as it will add the continuations on your behalf.
If you are using .NET framework 4.5, there's a new way to deal with Task objects.
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
DoSomethingAsync()
});
//this line will run in UI thread
throw new Exception("Some Exception");
}
And take note that any exception that's happening inside the Task object is swallowed inside the state machine created by the compiler when you use the async keyword. But this exception is set inside the Task object so basically you can still have reference to the exception using the Task object.
Something for your reference:
http://blog.stephencleary.com/2012/02/async-and-await.html
According to best practices if we can't escape the use of async void methods (especially for event handlers), the try catch should be implemented in that method. But you need to make sure that all the async methods you are calling have the async Task method signature in order to catch the exception being thrown.
If I declare my event handlers as async void, will they be called synchronously or asynchronously by the .NET framework?
I.e., given the following code:
async void myButton_click(object sender, EventArgs e) {
do stuff
await longRunning()
do more stuff
}
Can I be sure that the "do stuff" line will be executed on the GUI thread?
Event handlers will be called synchronously and doesn't wait(await) till the event handler completes, but waits till the event handler returns.
If previous sentence was confusing enough, I'll try to explain it clear. Asynchronous methods completes when all the await points are executed and the end of the method body has reached or any return statement is executed(Ignoring the exception). But asynchronous method returns as soon as you hit the first await statement for the Task which is not yet completed. In other words asynchronous method can return several times but can complete only once.
So now we know when does a asynchronous method completes and returns. Event handler will assume your method has completed as soon as it returns not when it actually completes.
As soon as your event handler reaches first await statement, it will return, if there are more methods attached to same event handler, it will continue executing them without waiting for the asynchronous method to complete.
Yes, do stuff will be executed in UI thread if the UI thread fires the event and yes do more stuff will also be executed in UI thread as long as longRunning().ConfigureAwait(false) isn't called.
They will be invoked just as any other non-async-await method is invoked:
Click(this, EventArgs.Empty);
Because this specific event handler is an async method the call would run synchronously until an await is reached and the rest would be a continuation. That means that do stuff is executed synchronously on the GUI thread. The caller then moves on without the knowledge that the async operation hasn't completed yet.
do more stuff would also be executed on the GUI thread, but for a different reason. The SynchronizationContext in a GUI environment makes sure the continuations would be posted to the single GUI thread, unless you explicitly tell it not to with await longRunning().ConfigureAwait(false)
Given the following code, why does ask.WhenAny never return when provided with a Task.Delay of 1 second? Technically I'm not sure if it does return after a extended amount of time, but it doesn't after 15 seconds or so after which I manually kill the process. According to the documentation I shouldn't be required to manually start the delayTask, and in fact I receive a exception if I try to do so manually.
The code is being called from the UI thread when a user selects a context menu item in a WPF application, although it works fine if I have the click method specified for the context menu item run this code in a new thread.
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
SomeMethod();
...
}
public void SomeMethod()
{
...
SomeOtherMethod();
....
}
public void SomeOtherMethod()
{
...
TcpClient client = Connect().Result;
...
}
//In case you're wondering about the override below, these methods are in
//different classes i've just simplified things here a bit so I'm not posting
//pages worth of code.
public override async Task<TcpClient> Connect()
{
...
Task connectTask = tcpClient.ConnectAsync(URI.Host, URI.Port);
Task delayTask = Task.Delay(1000);
if (await Task.WhenAny(connectTask, delayTask) == connectTask)
{
Console.Write("Connected\n");
...
return tcpClient;
}
Console.Write("Timed out\n");
...
return null;
}
If I change ContextMenuItem_Click to the following it works fine
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
new Thread(() => SomeMethod()).Start();
...
}
I predict that further up your call stack, you're calling Task.Wait or Task<T>.Result. This will cause a deadlock that I explain in full on my blog.
In short, what happens is that await will (by default) capture the current "context" and use that to resume its async method. In this example, the "context" is the WPF UI context.
So, when your code does its await on the task returned by WhenAll, it captures the WPF UI context. Later, when that task completes, it will attempt to resume on the UI thread. However, if the UI thread is blocked (i.e., in a call to Wait or Result), then the async method cannot continue running and will never complete the task it returned.
The proper solution is to use await instead of Wait or Result. This means your calling code will need to be async, and it will propagate through your code base. Eventually, you'll need to decide how to make your UI asynchronous, which is an art in itself. At least to start with, you'll need an async void event handler or some kind of an asynchronous MVVM command (I explore async MVVM commands in an MSDN article). From there you'll need to design a proper asynchronous UI; i.e., how your UI looks and what actions it permits when asynchronous operations are in progress.
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!
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.