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)
Related
I'm trying to wait for an asynchronous function to complete so that I can populate a ListView in my UI thread.
Here's the code
public Form1()
{
InitializeComponent();
Task t = new Task(Repopulate);
t.Start();
// some other work here
t.Wait(); //completes prematurely
}
async void Repopulate()
{
var query = ParseObject.GetQuery("Offer");
IEnumerable<ParseObject> results = await query.FindAsync();
if (TitleList == null)
TitleList = new List<string>();
foreach (ParseObject po in results)
TitleList.Add(po.Get<string>("title"));
}
TitleList = null in Form1() because Repopulate() hasn't been completed yet. Therefore I used Wait(). However, wait returns before function is even completed.
What am I doing wrong here?
You need to change the return type of your Repopulate method to return a task representing the asynchronous operation.
Also, you shouldn't perform asynchronous operations from your form constructor, since calling Task.Wait will cause your UI thread to block (and your application to appear unresponsive). Rather, subscribe to its Form.Load method, and perform the asynchronous operations there, using the await keyword to keep the event handler asynchronous. If you don't want the user to interact with the form until the asynchronous operation completes, then disable the form within the constructor and re-enable it at the end of the Load handler.
private async void Form1_Load(object sender, EventArgs e)
{
Task t = Repopulate();
// If you want to run its synchronous part on the thread pool:
// Task t = Task.Run(() => Repopulate());
// some other work here
await t;
}
async Task Repopulate()
{
var query = ParseObject.GetQuery("Offer");
IEnumerable<ParseObject> results = await query.FindAsync();
if (TitleList == null)
TitleList = new List<string>();
foreach (ParseObject po in results)
TitleList.Add(po.Get<string>("title"));
}
Update: For the benefit of future readers, I'm reworking my comments into the answer:
Task.Wait causes the calling thread to block until the task completes. Form constructors and event handlers, by their nature, run on the UI thread, so calling Wait within them will cause the UI thread to block. The await keyword, on the other hand, will cause the current method to relinquish control back to the caller – in the case of event handlers, this would allow the UI thread to continue processing events. The awaiting method (event handler) would then resume execution on the UI thread after the task completes.
Task.Wait will always block the calling thread, whether it's called from the constructor or the event handler, so one should avoid using it, especially when running on the UI thread. C# 5 introduced the async and await keywords to this end; however, they're only supported on methods, not constructors. This restriction underlies the main reason why you need to move your initialization code from the form constructor to an async event handler.
As to the reason why Task.Wait() returns prematurely: In your original code, task t represents the execution of the Task that you instantiated in your form constructor. This task runs Repopulate; however, the said method will return as soon as it encounters the first await statement, and execute the rest of its logic in a fire-and-forget fashion. This is the danger of using async void – you won't know when the asynchronous method has completed executing. (For this reason, async void should only be used for event handlers.) In other words, t.Wait() returns as soon as Repopulate hits its first await.
By changing the signature of Repopulate to async Task, you are now getting another task that represents the completion of its asynchronous execution, including the query.FindAsync() asynchronous call and the processing that succeeds it. When Task.Run is passed an asynchronous operation (Func<Task>) as argument, its returned task will wait for (unwrap) the inner task. This is why Task.Run should be used instead of Task.Start or Task.Factory.StartNew.
I need to raise an event without blocking the calling method, what is the way to do it?
1) Start a task and raise the event from within the task? :
//Body of listener function above
if (EventFound)
Task.Factory.StartNew(() =>
{
SendEvent();
});
2) Start a task from within the eventhandler:
public void OnEventRaised(....)
{
Task.Factory.StartNew(() =>
{
//Do lengthy stuff here
});
}
Does either block the calling function?
Neither of your examples blocks the caller.
In your first example the caller creates a new thread and invokes all subscribers sequentially (if there are more than one). In the second option the subscriber creates a thread, so each one will have it's own thread.
Please keep in mind that both options will crash the application if any of the event handlers fail.
You can get more relevant information from here: Raising events asynchronously
You can use Async Await keywords
http://www.codeproject.com/Tips/591586/Asynchronous-Programming-in-Csharp-using-async
At many blogs, tutorials and MSDN I can read that accessing UI elements from non-UI threads is impossible - ok, I'll get an unauthorized exception. To test it I've written a very simple example:
// simple text to which TextBlock.Text is bound
private string sample = "Starting text";
public string Sample
{
get { return sample; }
set { sample = value; RaiseProperty("Sample"); }
}
private async void firstButton_Click(object sender, RoutedEventArgs e)
{
await Job(); // asynchronous heavy job
commands.Add("Element"); // back on UI thread so this should be ok?
}
private async Task Job()
{
// I'm not on UI Thread ?
await Task.Delay(2000); // some other job
Sample = "Changed"; // not ok as not UI thread?
commands.Add("Element from async"); // also not ok?
}
I've a Task which is being run asynchronously. In that Task I want to change my property (which will raise PropertyChanged) and add element to ObservableCollection. As it is run async, I shouldn't be able to do that, but I get no exception and the code is working fine. Thus my doubts and misunderstanding:
why don't I get an exception?
is it ok to raise PropertyChanged in async Task?
is it ok to modify ObservableCollection in async Task, or should I return Task<ICollection> and after obtaining the result modify the ObservableCollection- Clear it and Fill it?
when am I in Task on UI thread and when not?
in the code above in firstButton_Click is it ok to manage UI elements after awaiting the Task? Am I always back on UI thread?
To test it more I've put my property change and collection modification in other thread:
System.Threading.Timer newThreadTimer = new System.Threading.Timer((x) =>
{
Sample = "Changed"; // not UI thread - exception
commands.Add("Element from async"); // not UI thread - exception
}, null, 1000, Timeout.Infinite);
In above code my thinking is ok - just after the first or second line I get an exception. But what with the first code? Is it only a luck that my Taskwas run on UI thread?
I suspect that this is very basic thing and my misunderstanding, but I need some clarification and thus this question.
When awaiting on a Task, the SynchronizationContext of the current thread is captured (specifically in the case of Task by the TaskAwaiter). The continutation is then marshaled back to that SynchronizationContext to execute the rest of the method (the part after the await keyword).
Lets look at your code example:
private async Task Job()
{
// I'm not on UI Thread ?
await Task.Delay(2000); // some other job
Sample = "Changed"; // not ok as not UI thread?
commands.Add("Element from async"); // also not ok?
}
When you await Task.Delay(2000), the compiler implicitly captures the SynchronizationContext, which is currently your WindowsFormsSynchronizationContext. When the await returns, the continuation is executed in the same context, since you didn't explicitly tell it not to, which is your UI thread.
If you changed your code to await Task.Delay(200).ConfigureAwait(false), the continuation would not be marshalled back to your current SynchronizationContext, and would run a ThreadPool thread, causing your UI element update to throw an exception.
In your timer example, the Elapsed event is raised via a ThreadPool thread, hence why you get an exception that you are trying to update an element which is controlled by a different thread.
Now, let's go over your questions one by one:
why don't I get exception?
As said, the await Task.Delay(2000) executed the Continuation on the UI thread, which made it possible to update your controls.
is it ok to Raise properties in async Task?
I am not sure what you mean by "Raise properties", but if you mean raise a INotifyPropertyChanged event, then yes, it is ok to execute them not in a UI thread context.
is it ok to modify ObservableCollecition in async Task, or should I
return Task and after obtaining the result modify Observable - Clear
it and Fill it?
If you have an async method and you want to update a UI bound element, make sure you marshal the continuation on the UI thread. If the method is called from the UI thread and you await its result, then the continuation will implicitly be ran on your UI thread. In case you want to offload work to a background thread via Task.Run and make sure your continuation is ran on the UI, you can capture your SynchronizationContext using TaskScheduler.FromCurrentSynchronizationContext() and explicitly pass it the continuation
when am I in Task on UI thread and when not?
A Task is a promise of work that will be done in the future. when you await on a TaskAwaitable from the UI thread context, then you are still running on the UI thread. You are not in the UI thread if:
Your async method is currently executing from a thread different then the UI thread (either a ThreadPool thread or a new Thread)
You offload work to a background ThreadPool thread using Task.Run.
in the code above in firstButton_Click is it ok to manage UI elements
after awaiting the Task? Am I always back on UI thread?
You will be back to the UI thread as long as you don't explicitly tell your code not to return to its current context using ConfigureAwait(false)
The PropertyChanged event is automatically dispatched to the UI thread by WPF so you can modify properties that raise it from any thread. Before .NET 4.5 this was not the case for ObservableCollection.CollectionChanged event and thus, adding or removing elements from a thread other than the UI one, would cause an exception.
Starting in .NET 4.5 CollectionChanged is also automatically dispatched to the UI thread so you don't need to worry about that. This being said, you'll still need to access UI elements (such as a button for instance) from the UI thread.
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.
Normally, when I want to cancel a backgroundWorker in C# I will do something like this:
while (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
autoResetEvent1.WaitOne(BGW_CANCEL_TIMEOUT);
}
In one application there are a number of backgroundWorkers. Is it valid to use a helper-function to cancel the backgroundWorkers, like so?
CancelBackgroundWorker(BackgroundWorker bgw, AutoResetEvent are)
{
while (bgw.IsBusy)
{
bgw.CancelAsync();
are.WaitOne(BGW_CANCEL_TIMEOUT);
}
}
My concern is that instead of passing the objects in question into the function, copies are made, thereby defeating the purpose of having the function. The main purpose of having the function is to reduce code space and make the application code more readable/maintainable.
No, this isn't okay. It causes deadlock when the BGW has a RunWorkerCompleted event handler. That handler cannot run until the main thread goes idle and re-enters the message loop. The IsBusy property will stay True until that event handler completes.
You have a time-out on the WaitOne call so at least your program won't hang completely. But when WaitOne() returns, the BGW is not yet completed, the RWC event handler hasn't run yet. The only practical alternative is for the RWC event handler to do whatever needs done when the cancellation is complete.
Your code is wrong.
Calling CancelAsync merely sets the CancellationPending flag on the BackgroundWorker to true. Your code in the DowWork event is expected to periodically check this flag and stop if the flag is true.
Calling CancelAsync many times will not do any good, and there shouldn't be any reason to freeze the UI thread until it actually cancels.