How to call an async method, and update the UI from there? - c#

Recently I've finished designing a PagedDataGridView custom control, it works very well, but now I'd like to improve its performance.
How?, well, I've seen some UI blocking when showing new pages.
In short words, this:
public class PagedDataGridView() : UserControl
{
Paging paging = new Paging();
public PagedDataGridView()
{
paging.LoadDataClientMethod = LoadDataOnGrid;
}
private void LoadDataOnGrid()
{
// Some heavy set data source here, using functions from 'paging' object
}
}
What I'm trying to do (using the async / await pattern):
That async method DoPaging pauses until the await 'LoadDataOnGrid' is complete, that way the UI thread is not blocked, be asynchronous.
// Class that handles paging methods, variables,
// also offers navigation controls, such as buttons, labels, etc.
internal class Paging
{
// Represents the method that code client uses to load its own data
public Action LoadDataClientMethod;
// HERE:
private async Task DoPaging()
{
// some calculations
if (LoadDataClientMethod != null)
{
// I realizad that calling Run method, runs it out of context
// How to run this method, and update the UI
await Task.Run(() => LoadDataClientMethod());
}
// Update controls, showing current page, etc
UpdateUI();
}
// Navigation buttons
private void btnGoNextPage(object sender, EventArgs e)
{
// go next page calculations
// Then how to call the async method
DoPaging(); // -> doing this, VS shows a warning:
/* Because this call is not awaited, the current method
continues to run before the call is completed */
}
}
I'm just starting to learn about async - await coding, any correction or advice will be greatly appreciated, thanks.

There is a big difference between:
private void btnGoNextPage(object sender, EventArgs e)
{
DoPaging();
}
and
private async void btnGoNextPage(object sender, EventArgs e)
{
await DoPaging();
}
Exception handling. If the former throws an exception, two things might happen:
If you're using .NET 4.0, the swallowed task will be re-thrown from the Finalizer thread and will cause your application to crash
If you're using .NET 4.5, the task will be swallowed and will go un-noticed and will not be re-thrown at all, thus possibly entering your application in a corrupted state which you wont be aware of.
in the latter example, the exception will propogate to the await point, and you can handle it gracefully by adding a try-catch block.
As a side note, i asked you in the comments what kind of work is being done that is blocking your UI thread, and you said that you are making a call to your database to retrieve data.
Work being done against a database is IO bound work, and most providers expose async endpoints to access data, such as Entity Framework, ADO.NET, etc. You can make use of that naturally async behavior by not using any threadpool threads to do the work for you (with Task.Run as you're doing in your example). You can do that when you go "async all the way", and your database query can be used with the await keyword. That way, while the query is retrieving the data, the thread that invoked the query (in your example, the UI thread) is freed and can do more work, thus your UI will stay responsive.
I suggest you look into that and see if your database provider has those async endpoints.

Just add async to the button click event handler method and await the call to DoPaging():
private async void btnGoNextPage(object sender, EventArgs e)
{
await DoPaging();
}
The difference between doing it like this rather than the way you had that gives the warning (and is in fact why the warning is given) is that if you added any code after the call to DoPaging() in the event handler it would now occur after the task has complete, whereas before it would execute immediately after the call.

Related

Modify UI element from third party async lib callback

I'm using a third party lib which offer asynchronous methods.
The lib can receive callbacks that will be executed during asynchronous operations.
I'm using this lib from a Windows Forms app and passing a callback to update UI state.
However, it seems the callback is being called from a thread other then the UI's.
I've digged in the third party lib implementation, and they don't seem to be doing anything strange.
Actually, they chain some async calls internally using ConfigureAwait(false), which seems to be the recommeded way of awaiting tasks inside libs.
I was able to reproduce the behavior with minimal code:
public class ThirdPartyLibClass
{
public event EventHandler<EventArgs> StuffDone;
public async Task DoStuffAsync()
{
await Task.Delay(100).ConfigureAwait(false);
StuffDone?.Invoke(this, EventArgs.Empty);
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button2_Click(object sender, EventArgs e)
{
var thirdPartyLibClass = new ThirdPartyLibClass();
thirdPartyLibClass.StuffDone += (s,e2) => this.textBox1.Text = "This will fail because it will be executed in a thread other than the UI's.";
await thirdPartyLibClass.DoStuffAsync();
}
}
I know how to get around the issue.
The point is: it seems counterintuitive to force a lib consumer to assume that callbacks in async libs might not be called in the original synchronization context.
I've considered the following ways to better deal with this, from the lib maintainer's perspective:
Remove the ConfigureAwait(false)
Capture the synchronization context and raise any events / call any callbacks on it
My questions is: should async libs make sure that callbacks are executed in their original synchronization context, or should consumers of async libs always consider that events/callbacks might be called from unknown threads?
Update
Based on Stephen's answer, I realized my example was a bit biased towards watch completion, which is not the case I'd like to bring to light.
I've provided an updated example below:
public class ThirdPartyLibClass
{
private Action doSomeMoreStuffForMeAsync;
public ThirdPartyLibClass(Action doSomeMoreStuffForMeAsync)
{
this.doSomeMoreStuffForMeAsync = doSomeMoreStuffForMeAsync;
}
public async Task DoStuffAsync()
{
await Task.Delay(100).ConfigureAwait(false);
doSomeMoreStuffForMeAsync();
await Task.Delay(100).ConfigureAwait(false);
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button2_Click(object sender, EventArgs e)
{
var thirdPartyLibClass = new ThirdPartyLibClass(
() => this.textBox1.Text = "This will fail because it will be executed in a thread other than the UI's."
);
await thirdPartyLibClass.DoStuffAsync();
}
}
To be more concrete, the third party library I'm using is Stateless.
Upon the state machine construction, it receives delegates to be called on state transitions.
However, I'm finding out that I cannot rely on those delegates being called in the original synchronization context, which is a real bummer, as I essentially wanted to use a state machine to organize better my GUI applications.
Asynchronous libraries using async/await usually don't use events to signal completion at all. Using events to signal completion is likely a holdover from an old version of the API, which was probably using the Event-Based Asynchronous Pattern.
If that's correct, and if the APIs return tasks, then it seems like your library is exposing some kind of mixed API. In that case, you should just ignore any completion events and use await to handle completion instead:
private async void button2_Click(object sender, EventArgs e)
{
var thirdPartyLibClass = new ThirdPartyLibClass();
await thirdPartyLibClass.DoStuffAsync();
this.textBox1.Text = "This works fine.";
}
should async libs make sure that callbacks are executed in their original synchronization context, or should consumers of async libs always consider that events/callbacks might be called from unknown threads?
Most events are just raised on a thread pool context, and it is up to the handler to do any thread marshalling necessary.
There are a few exceptions; some objects capture SynchronizationContext.Current at some point and use that to raise events - but in that case you need to be sure to document when it is captured (e.g., the constructor vs DoStuffAsync). There was also an old pattern based on ISynchronizeInvoke with a property commonly called SynchronizingObject. But in modern code, events are generally expected to be raised on a thread pool thread.

Task.Wait for async method passes during application startup while it causes deadlock in WPF button handler

The behavior of Task.Wait() is unexpectedly different depending on the "environment" where invoked.
Calling Task.Wait() during application startup with below async method TestAsync passes (doesn't cause a deadlock) while the same code blocks when called from within a WPF Button handler.
Steps to reproduce:
In Visual Studio, using the wizard, create a vanilla WPF .NET framework application (e.g. named WpfApp).
In the App.xaml.cs file of the app file paste below Main method and TestAsync method.
In the project properties set Startup object to WpfApp.App.
In the properties of App.xaml switch Build Action from ApplicationDefinition to Page.
public partial class App : Application
{
[STAThread]
public static int Main(string[] args)
{
Task<DateTime> task = App.TestAsync();
task.Wait();
App app = new App();
app.InitializeComponent();
return app.Run();
}
internal static async Task<DateTime> TestAsync()
{
DateTime completed = await Task.Run<DateTime>(() => {
System.Threading.Thread.Sleep(3000);
return DateTime.Now;
});
System.Diagnostics.Debug.WriteLine(completed);
return completed;
}
}
Observe that the application starts properly (after 3sec delay) and that the "completed" DateTime is written to debug output.
Next create a Button in MainWindow.xaml with Click handler Button_Click in MainWindow.xaml.cs
public partial class MainWindow : Window
{
...
private void Button_Click(object sender, RoutedEventArgs e)
{
Task<DateTime> task = App.TestAsync();
task.Wait();
}
}
Observe that after clicking the Button, the application is deadlocked.
Why can't it pass in both cases?
Is there a way to change invocation (e.g. using ConfigureAwait at the correct task or somehow setting SynchronizationContext or whatever) so that it behaves identical in both invocations, but still synchronously waits for completion?
Update on limitations of the solution.
The async method like TestAsync comes from a library that cannot be changed.
The invocation code of the TestAsync method is nested within a callstack that cannot be changed either, and the code outside the callstck makes use of the returned value of the async method.
Ultimately the solution code has to convert the async method to run synchronous by not changing the method nor the caller.
This works well within UT code (NUnit) and during application startup, but no more within a handler of WPF.
Why?
There are a couple of different ways that you can handle this situation, but ultimately the reason there is a deadlock in one situation and not the other is that when called in the Main method SynchronizationContext.Current is null, so there isn't a main UI context to capture and all async callbacks are handled on thread pool threads. When called from the button, there is a synchronization context which is captured automatically, so all async callbacks in that situation are handled on the main UI thread which is causing the deadlock. In general the only way you won't get that deadlock is by forcing the async code to not capture the synchronization context, or use async all the way up and don't synchronously wait from the main UI context.
you can ConfigureAwait(false) inside of your TestAsync method so that it doesn't capture the synchronization context and try to continue on the main UI thread (this is ultimately what is causing your deadlock because you are calling task.Wait() on the UI thread which is blocking the UI thread, and you have System.Diagnostics.Debug.WriteLine(completed); that is trying to be scheduled back onto the UI thread because await automatically captures the synchronization context)
DateTime completed = await Task.Run<DateTime>(() => {
System.Threading.Thread.Sleep(3000);
return DateTime.Now;
}).ConfigureAwait(false);
You can start the async task on a background thread so that there isn't a synchronization context to capture.
private void Button_Click(object sender, RoutedEventArgs e)
{
var task = Task.Run(() => App.TestAsync());
var dateTime = task.Result;
}
you can use async up the whole stack
private async void Button_Click(object sender, RoutedEventArgs e)
{
Task<DateTime> task = App.TestAsync();
var dateTime = await task;
}
Given how you are using it, if you don't have to wait until the task is done, you can just let it go and it will finish eventually, but you lose the context to handle any exceptions
private void Button_Click(object sender, RoutedEventArgs e)
{
//assigning to a variable indicates to the compiler that you
//know the application will continue on without checking if
//the task is finished. If you aren't using the variable, you
//can use the throw away special character _
_ = App.TestAsync();
}
These options are not in any particular order, and actually, best practice would probably be #3. async void is allowed specifically for cases like this where you want to handle a callback event asynchronously.
From what I understand, in .NET many of the front ends have a single UI thread, and therefore must be written async all the way through. Other threads are reserved and utilized for things like rendering.
For WPF, this is why use of the Dispatcher and how you queue up work items is important, as this is your way to interact with the one thread you have at your disposal. More reading on it here
Ditch the .Result as this will block, rewrite the method as async, and call it from within the Dispatch.Invoke() and it should run as intended
Why can't it pass in both cases?
The difference is the presence of a SynchronizationContext. All threads start out without a SynchronizationContext. UI applications have a special UI thread(s) and at some point they need to create a SynchronizationContext and install it on that thread(s). Exactly when this happens isn't documented (or consistent), but it has to be installed at the point the UI main loop starts.
In this case, WPF will install it (at the latest) within the call to Application.Run. All user invocations from the UI framework (e.g., event handlers) happen within this context.
The blocking code deadlocks with the context because this is the classic deadlock situation, which requires three components:
A context that only allows one thread at a time.
An asynchronous method that captures that context.
A method also running in that context that blocks waiting for that asynchronous method.
Before the WPF code installed the context, condition (1) wasn't met, and that's why it didn't deadlock.
Is there a way to change invocation (e.g. using ConfigureAwait at the correct task or somehow setting SynchronizationContext or whatever) so that it behaves identical in both invocations, but still synchronously waits for completion?
We-ell...
This is a rephrasing of "how do I block on asynchronous code", and there's no good answer for that. The best answer is to not block on asynchronous code at all; i.e., use async all the way. Especially since this is GUI code, I'd say for the sake of UX you really want to avoid blocking. Since you're on WPF, you may find a technique like asynchronous MVVM data binding useful.
That said, there are a few hacks you can use if you must. Using ConfigureAwait is one possible solution, but not one I recommend; you'd have to apply it to all awaits within the transitive closure of all methods being blocked on (Blocking Hack). Or you can shunt the work to the thread pool (Task.Run) and block on that (Thread Pool Hack). Or you can remove the SynchronizationContext - unless the code being blocked on manipulates UI elements or bound data. Or there are even more dangerous hacks that I really can't recommend at all (Nested Message Loop Hack).
But even after putting in all the work for a hack, you'll still end up blocking the UI. The hacks are hard precisely because they're not recommended. It's quite a bit of work to give your users a worse experience. The far, far better solution (for your users and future code maintainers) is to go async all the way.

Task WhenAll combined with ContinueWith not work as expected

I have a Winform project that inside of the winform class I have a property called DataBindingTasks like so.
// create a task list to determine when tasks have finished during load
protected List<Task> DataBindingTasks = new List<Task>();
I have several async void methods that I am calling in the winform "Load" event that are all similar to the following.
private async void BindSomething(int millSecToWait)
{
var someTask = Task.Factory.StartNew(() =>
{
// do some work
System.Threading.Thread.Sleep(millSecToWait);
// return some list for binding
return new List<int>();
});
// add the task to the task list
DataBindingTasks.Add(someTask);
// wait until data has loaded
var listToBind = await someTask;
// bind the data to a grid
}
I am calling the BindSomething methods on load.
I say methods because there are several of these binding types of methods that are called on load.
private void Form_Load(object sender, EventArgs e)
{
// async bind something and let UI continue
// fire and forget
BindSomething(5000);
BindSomething(8000);
BindSomething(2000);
BindSomething(2000);
// code to execute when all data binding tasks have completed
Task.WhenAll(DataBindingTasks).ContinueWith((x) =>
{
// Do something after all async binding tasks have completed
});
}
EXCEPT the ContinueWith code is executing even though all the tasks have not completed.
Here is a screen shot showing that all task are not complete.
UPDATED 10/29
The problem is obviously deeper than the sample code above and the sample code above does not fully explain the true scenario.
I will try to explain in greater detail but try to not make it to long.
This is a Winform application.
We have created a base winform "BaseForm" that all other winforms will inherit from.
We have overridden the "OnLoad" event in the "BaseForm" so that we can call a new method that all inherited forms will have called "LoadData".
Since "LoadData" can have async method calls, the base form needs to know when the "LoadData" method is finished.
So in the base form was have some of the following:
protected List<Task> DataBindingTasks = new List<Task>();
public event EventHandler DataBindingTasksComplete;
protected void OnDataBindingTasksComplete(EventArgs e)
{
if (DataBindingTasksComplete != null)
{
DataBindingTasksComplete(this, e);
}
// now clear the list
DataBindingTasks.Clear();
}
// NOTE: this is inside the OnLoad called before base.OnLoad(e)
Task.WhenAll(DataBindingTasks).ContinueWith((x) =>
{
OnDataBindingTasksComplete(EventArgs.Empty);
});
The hope was that all inherited forms would add any of their "async" tasks to this list so that the base form could fire the "DataBindingTasksComplete" event so they would know that form has finished loading.
The problem "as perceived to us at the time of the issue" was that the "WhenAll().ContinueWith" was not waiting until all the tasks on the list had completed.
BUT as someone noted, the list might have changed.
So here is most likely what happened.
There are 4 "BindSomething" methods that are marked async all called from the Form_Load
The 2nd or so line down inside the "BindSomething" method is used to add a task to the "BaseForm.DataBindingTasks" list.
Since each of these calls are marked async, the Form_Load continues to call all 4 as a "fire and forget".
After that, it returns back to the BaseForm OnLoad which then looks at the "DataBindingTasks" list to see if all tasks have completed.
My best guess is that one of the "BindSomething" methods was in the middle of adding its task to the list yet the Base.OnLoad has already started looking at the list.
I could add 4 "fake" tasks (like thread sleep) to the list even before calling the "BindSomething" methods as "place holders" and then inside the "BindSomething" methods swap out the "fake" tasks with the "real" tasks.
This seams messy and most likely will cause other issues.
The most likely fix is to not use a task list / WhenAll.ContinueWith and instead call the load data with "await" and then raise the event on the next line.
The async void methods are called as fire-and-forget, and there is no way to wait for them, that's why your delegate don't wait properly - it simply can't do that. So you need some changes in your code.
Update: #Servy noted the main problem in your code which I've missed, thanks for him:
DataBindingTasks.Add(someTask);
This operation isn't thread-safe! You simply losing some of your tasks during parallel calls for Add method. You need to change this: by using lock, by using ConcurrentCollection or by using data separation: assign a task to array by different indexes so parallel tasks aren't intersect each other.
First of all, you shouldn't use the StartNew in this case, use the Task.Run, otherwise you can met some problems in your app.
Second thing is that you can make the Load method async and await it, so your UI wouldn't freeze, and you can switch the signature for your BindSomething methods to became awaitable, as #digimunk mentioned:
// note that we return the task here
private async Task BindSomething(int millSecToWait)
{
// use Task.Run in this case
var someTask = Task.Run(() =>
{
// Some work
System.Threading.Thread.Sleep(millSecToWait);
// return some list for binding
return new List<int>();
});
DataBindingTasks.Add(someTask);
// wait until data has loaded
var listToBind = await someTask;
// bind the data to a grid
}
// async void for the event handler
private async void Load()
{
// start tasks in fire-and-forget fashion
BindSomething(5000);
BindSomething(8000);
BindSomething(2000);
// code to execute when all data binding tasks have completed
await Task.WhenAll(DataBindingTasks);
// Do something after all binding is complete
}
In this case you can await the Load method safely.
You don't need .ContinueWith(). Just await the Task.WhenAll(), and then put whatever code you want to run after it under it. Also, change the "void" in the method signature to "async Task".

Fire & Forget method using Task.Run not working

I read a lot of codes trying to use Task.Run without success.
What I want to achive:
In an ASP.NET WebForm event (click event handler) call a Fire & Forget method (not block the current flow of execution).
What I tried and don't understant why it's not working:
First Version:
protected void btn_Click(object sender, EventArgs e)
{
// Some actions
// Should insert a record in database --> OK
//Tried this call with and without ConfigureAwait(false)
Task.Run(() => MyClass.doWork()).ConfigureAwait(false);
// Should insert a record in database --> OK
// Some actions not blocked by the previous call
}
public static class MyClass
{
public static void doWork()
{
// Should insert a record in database --> NOT INSERTED
}
}
Second Version:
protected void btn_Click(object sender, EventArgs e)
{
// Some actions
// Should insert a record in database --> OK
Bridge.call_doWork();
// Should insert a record in database --> OK
// Some actions not blocked by the previous call
}
public static class Bridge
{
public static async Task call_doWork()
{
//Tried this call with and without ConfigureAwait(false)
await Task.Run(() => MyClass.doWork()).ConfigureAwait(false);
}
}
public static class MyClass
{
public static void doWork()
{
// Should insert a record in database --> NOT INSERTED
}
}
So I call the Fire & Forget method, which should insert a record in the database, but there's no record inserted.
The inserts before and after the call to the Fire & Forget method are done.
I don't know how to resolve my issue.
HttpContext will not be available in threads other than the main thread, so you can't depend on it.
But you can pass data from HttpContext to your method when you start the task. For example:
Task.Run(() => MyClass.doWork(HttpContext.Current.Session["somedata"])).ConfigureAwait(false);
Why didn't I get an EventViewer event with the HttpContext call? I got one when using HostingEnvironment.QueueBackgroundWorkItem instead of Task.Run.
OK, first off, if you have QueueBackgroundWorkItem available, why would you ever use Task.Run for fire-and-forget work?
As I describe on my blog, using Task.Run for fire-and-forget on ASP.NET is a really bad idea! QueueBackgroundWorkItem is the minimum viable solution, and that's only if you accept unreliability.
QueueBackgroundWorkItem does a couple of things for you beyond Task.Run: it registers the work with the ASP.NET runtime (which minimizes but does not eliminate the possibility that the work will not complete), and it catches exceptions and logs them for you (which is why you were seeing the event notifications).
So, you were seeing an event for your exception because QBWI was doing that for you. Whereas with the Task.Run code, the exception would be caught and placed on the returned Task (as it should be), and then your code completely ignored that task, thus silently swallowing the exception.
Yes! I use HttpContext to get the Session. But I can't change that without changing a lot of code.
As others have noted, HttpContext is only valid within a request context. So, when you explicitly run background code, of course it doesn't have a request context. Background code must be independent of requests, by definition.
But there's one other really important consideration that I think you're overlooking:
public static void doWork()
{
// Should insert a record in database --> NOT INSERTED
}
Are you sure that your app is perfectly OK if doWork once in a while doesn't execute? Because that's what happens to background tasks. ASP.NET was designed to respond to requests, not run background tasks.
So, every once in a blue moon, the record that should have been inserted by doWork won't show up. If this is unacceptable, then you shouldn't be doing fire-and-forget.

Silverlight and problems with async call

I have some code that works as follows:
App.xaml calls a SetUp() method which populates a local collection using async calls and exposes the collection as a public property.
That's all good.
Now I make an instance of the first page in my Silverlight app like so
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Summary();
}
In the constructor of Summary() I expect those async calls to have been complete and my collection to be filled but the async calls have not yet complete. Even if I do a Thread.Sleep(100000....) before i make an instance on Summary() this is the case
And the thing is that until the constructor of Summary() is exited and the UI displayed to the user my async calls do not get kicked off!
What!!!
Is there anything I can do about that or is that just the way asyn calls work i.e. they wait until the current until of work finished before firing?
This is how I work round this situation (I'll use simple string download as an example):-
private void Application_Startup(object sender, StartupEventArgs e)
{
WebClient web = new WebClient();
web.DownloadStringCompleted += (s, args) =>
{
// Do stuff with args.Result);
this.RootVisual = new Summary();
};
web.DownloadStringAsync(new Uri("SomeUrl.txt", UriKind.Relative));
}
Note that the construction of Summary and the assignment to RootVisual are defered until the asynchronous download is complete.
Silverlight was specifically designed to not have any blocking operations -- that's why you have to do async in the first place. What you really have to do is make your app run properly while waiting for the async completion handler to execute.
This is by design. The alternative would be that the user has to wait longer before he sees anything at all.
If you really want to prevent showing an incomplete summery then first shown another page with 'waiting' or a progress bar. Fire the async events from that page or its parent. Then show the Summary when the async call returns.
To deal with an incomplete Summary you might want to use ICommand
I know this is an old thread, but for all following reader i want to provide my experience with a similar problem. If i understood you correctly - the reason why your async calls do not complete is because you block the main thread. I ran into the same problem, if you block the main thread none of the other threads continue. The solution was to do the async calls inside a backgroundworker and show, like pauldendulk said, a waiting - page. Use Debug.WriteLine() to monitor the process in the output.

Categories