Group multiple async operations on the UI thread - c#

I have this:
BusyState.SetBusy("Updating Calendar Data");
Dispatcher.CurrentDispatcher.Invoke(new Action(async () =>
{
// calling "update" will hit servers outside my control and may take time
await PublicCalendars.Update();
await PrivateCalendars.Update();
// clearing the busy state should only happen when both update tasks are finished
BusyState.ClearBusy();
}));
The vars PublicCalendars and PrivateCalendars are both extending ObservableCollection and are populated during the call to update. The collections are bound to some WPF GUI so adding items must happen from the UI thread.
I'd like to remove the await's and let both calls run simultaneously. How can I do this and still have my busy state clear when both tasks are finished?

The strength of Tasks is that they can be easily composed. So, if you want to get a Task that represents the completion of both Tasks, you can use a method that does that: Task.WhenAll():
await Task.WhenAny(PublicCalendars.Update(), PrivateCalendars.Update());
BusyState.ClearBusy();
And you could also get a very similar behavior by yourself by saving the returned Tasks and then awaiting them:
var publicTask = PublicCalendars.Update();
var privateTask = PrivateCalendars.Update();
await publicTask;
await privateTask;
BusyState.ClearBusy();

Related

Universal Windows UI Responsiveness with Async/Await

Please help me understand how to properly await long executing tasks to keep the UI responsive in a Universal Windows application.
In the code below OperateSystem is a model class which inherits ObservableObject. OperateSystem.GetLatestDataFromAllDevices connects to a variety of instruments, collects data, and updates class properties with the information from the instruments. The views update with values from Operate System.
The UI is not responsive while the dispatcher.RunAsync task is running, I added a Thread.Sleep(5000) to GetLatestDataFromAllDevices() to make sure and it locks up the UI for 5 seconds. Without the await Task.Delay(refreshTimer) the UI never updates (I'm assuming it instantly goes back into the GetLatestDataFromAllDevies before the UI can update). Setting the refreshTimer to 1ms allows the UI to update, but I know that's a workaround for another issue that needs to be fixed.
public ProductionViewModel()
{
OperateSystem = new OperateSystem();
StartButtonCommand = new RelayCommand(StartMeasurementSystem);
StopButtonCommand = new RelayCommand(StopMeasurementSystem);
if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
}
}
private async void StartMeasurementSystem()
{
stopRequest = false;
StopButtonEnabled = true;
StartButtonEnabled = false;
while (!stopRequest)
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => OperateSystem.GetLatestDataFromAllDevices(ConfigurationSettingsInstance));
await Task.Delay(refreshTimer);
}
}
In OperateSystem
internal void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
GetDataInstrument1(configurationSettings);
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
Each of the GetDataInstrumnet methods connect to an instrument, gathers data, performs some scaling/formatting, and updates a class property with the current value.
I followed other S.O. answers to use the dispatcher.RunAsync as using other async methods I would get thread mashalling errors. But now I think the dispatcher is just marshaling these tasks on the UI thread anyway so it still blocks UI udpates.
To recreate the thread marshalling errors, I made GetLatestDataFromAllDevices async, and awaited a method executed as a task.
internal async void GetLatestDataFromAllDevices(ConfigurationSettings configurationSettings)
{
await Task.Run(()=>GetDataInstrument1(configurationSettings));
GetDataInstrument2(configurationSettings);
GetDataInstrument3(configurationSettings);
GetDatainstrumetn4(configurationSettings);
}
This results in:
System.Exception: 'The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))'
I've refactored in circles a few times and keep running into either thread marshaling errors or an unresponsive UI, what's a good way to get this done?
I've refactored in circles a few times and keep running into either thread marshaling errors or an unresponsive UI, what's a good way to get this done?
Since you have an unresponsive UI, you must push the work to a background thread (e.g., Task.Run).
For marshalling updates back to the UI thread, I recommend (in order of preference):
Using the return value of asynchronous methods. E.g., MyUiProperty = await Task.Run(() => MyBackgroundMethod(...));.
Using Progress<T> to get multiple values from asynchronous methods. E.g., var progress = new Progress<string>(update => MyUiProperty = update); await Task.Run(() => MyBackgroundMethod(..., progress));.
Capturing a SynchronizationContext in your background classes and using that for sending updates to the UI thread. This is the least recommended because it results in your background driving your UI instead of the other way around.

Updating bound values aynchronously

I have the following asynchronous method in a WPF project:
private async void RecalculateRun(Guid run_number)
{
// kick off the Full recalculation
//
await FullRecalcAsync(run_number);
// When thats done, asyncronously kick off a refresh
//
Task RefreshTask = new Task(() => RefreshResults());
await RefreshTask;
}
The first await does a load of calculations and the second takes the results and updates some bound variables. I wasn't expecting the UI to update during the second await, but I was expecting it to do so when it finished. Of course, this doesn't happen. Also, I'd just like to point out that if I call RefreshTask synchronously after the first await, it works fine.
You're creating a task without starting it. You need to call Start:
Task RefreshTask = new Task(() => RefreshResults());
RefreshTask.Start();
await RefreshTask;
Or better off use Task.Run
await Task.Run(() => RefreshResults());
Using the Task constructor directly is usually discouraged:
In general, I always recommend using Task.Factory.StartNew unless the particular situation provides a compelling reason to use the constructor followed by Start. There are a few reasons I recommend this. For one, it's generally more efficient
From "Task.Factory.StartNew" vs "new Task(...).Start"

await task.delay helps in UI refresh faster, but how?

I have a view model that is fetching a row of records and displaying on the Windows phone UI.. This view model method which fetches the data is doing a lot of Tasks, all marked with Await operations..
Looks like below:
async Task GetData()
{
var dataCollection = await GetSomeData();
await DoThis();
await DoThat();
}
The UI refreshes after the 'DoThis' call is invoked.
Now I just observed that if I introduce a Task.Delay in the code before other Tasks are done, the UI is refreshed immediately.. Which was my original Goal, to refresh the UI immediately after 'GetSomeData' Returns and then fire the other Tasks.
Looks like below:
async Task GetData()
{
var dataCollection = await GetSomeData();
await Task.Delay(100);
await DoThis();
await DoThat();
}
So what I understand from this, is that the UI thread gets opportunity to refresh after a call to Task.Delay is made. However without Task.Delay, if DoThis is called, some time is lost before it finds the first awaitable method in 'DoThis' so that it can return and continue with UI Refresh.
My questions are:
Have I understood it correct?
Is it safe to put this in production code?
Thanks in advance and hope I have explained clearly..
Pr
Below is Details of These methods as without those it is not clear what is going in the program.. :(
async Task<ObservableCollection<MyModel>> GetSomeData()
{
return await Task.Run(() =>
{
using (var db = new MainModelDataContext())
{
List<MyModel> temp =
db.Models.Where(Some condition)
.Take(30)
.ToList();
var returnCollection = new ObservableCollection<MyModel>(temp);
return returnCollection;
}
}
The ObservableCollection is bound to a list control on the UI Page. This method is being called by the page view model.
async Task DoThis()
{
// do some data processing and then post that to the Server
// this is the first awaitable method after the data processing
await (an HttpClientHandler).PostAsync();
}
Task DoThat() also follows the same flow as DoThis.. The data processing is also wrapped in async-await Tasks and they are just working on some class properties.
Hope I am clear.. Thanks again all
When you call Task.Delay, you return control to the UI message loop for those 100 milliseconds, and the UI message loop has the opportunity to process more messages from its queue. That's why you're experiencing the "refreshing" effect. If your GetSomeData method is truely asynchronous, your UI should remain responsive during its operation, and when it completes, it will continue to execute the next await.
If this effect doesn't happen, then that means your methods aren't really asynchronous, and its more likely that they are running a costly operation in a synchronous fashion which is blocking your UI thread.
Before putting this into production, you have to look into why you need Task.Delay to refresh your UI.

How to let the UI refresh during a long running *UI* operation

Before you flag my question as being a duplicate, hear me out.
Most people have a long running non-UI operation that they are doing and need to unblock the UI thread. I have a long running UI operation which must run on the UI thread which is blocking the rest of my application. Basically, I am dynamically constructing DependencyObjects at run time and adding them to a UI component on my WPF application. The number of DependencyObjects that need to be created depends upon user input, of which there is no limit. One of the test inputs I have has about 6000 DependencyObjects that need to be created and loading them takes a couple minutes.
The usual solution of using a background worker in this case does not work, because once the DependencyObjects are created by the background worker, they can no longer be added to the UI component since they were created on the background thread.
My current attempt at a solution is to run the loop in a background thread, dispatch to the UI thread for each unit of work and then calling Thread.Yield() to give the UI thread a chance to update. This almost works - the UI thread does get the chance to update itself a couple times during the operation, but the application is still essentially blocked.
How can I get my application to keep updating the UI and processing events on other forms during this long running operation?
EDIT:
As requested, an example of my current 'solution':
private void InitializeForm(List<NonDependencyObject> myCollection)
{
Action<NonDependencyObject> doWork = (nonDepObj) =>
{
var dependencyObject = CreateDependencyObject(nonDepObj);
UiComponent.Add(dependencyObject);
// Set up some binding on each dependencyObject and update progress bar
...
};
Action background = () =>
{
foreach (var nonDependencyObject in myCollection)
{
if (nonDependencyObject.NeedsToBeAdded())
{
Dispatcher.Invoke(doWork, nonDependencyObject);
Thread.Yield(); //Doesn't give UI enough time to update
}
}
};
background.BeginInvoke(background.EndInvoke, null);
}
Changing Thread.Yield() to Thread.Sleep(1) seems to work, but is that really a good solution?
Sometimes it is indeed required to do the background work on the UI thread, particularly, when the majority of work is to deal with the user input.
Example: real-time syntax highlighting, as-you-type. It might be possible to offload some sub-work-items of such background operation to a pool thread, but that wouldn't eliminate the fact the text of the editor control is changing upon every new typed character.
Help at hand: await Dispatcher.Yield(DispatcherPriority.ApplicationIdle). This will give the user input events (mouse and keyboard) the best priority on the WPF Dispatcher event loop. The background work process may look like this:
async Task DoUIThreadWorkAsync(CancellationToken token)
{
var i = 0;
while (true)
{
token.ThrowIfCancellationRequested();
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
// do the UI-related work
this.TextBlock.Text = "iteration " + i++;
}
}
This will keep the UI responsive and will do the background work as fast as possible, but with the idle priority.
We may want to enhance it with some throttle (wait for at least 100 ms between iterations) and better cancellation logic:
async Task DoUIThreadWorkAsync(CancellationToken token)
{
Func<Task> idleYield = async () =>
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
var cancellationTcs = new TaskCompletionSource<bool>();
using (token.Register(() =>
cancellationTcs.SetCanceled(), useSynchronizationContext: true))
{
var i = 0;
while (true)
{
await Task.Delay(100, token);
await Task.WhenAny(idleYield(), cancellationTcs.Task);
token.ThrowIfCancellationRequested();
// do the UI-related work
this.TextBlock.Text = "iteration " + i++;
}
}
}
Updated as the OP has posted a sample code.
Based upon the code you posted, I agree with #HighCore's comment about the proper ViewModel.
The way you're doing it currently, background.BeginInvoke starts a background operation on a pool thread, then synchronously calls back the UI thread on a tight foreach loop, with Dispatcher.Invoke. This only adds an extra overhead. Besides, you're not observing the end of this operation, because you're simply ignoring the IAsyncResult returned by background.BeginInvoke. Thus, InitializeForm returns, while background.BeginInvoke continues on a background thread. Essentially, this is a fire-and-forget call.
If you really want to stick to the UI thread, below is how it can be done using the approach I described.
Note that _initializeTask = background() is still an asynchronous operation, despite it's taking place on the UI thread. You won't be able to make it synchronous without a nested Dispatcher event loop inside InitializeForm (which would be a really bad idea because of the implications with the UI re-entrancy).
That said, a simplified version (no throttle or cancellation) may look like this:
Task _initializeTask;
private void InitializeForm(List<NonDependencyObject> myCollection)
{
Action<NonDependencyObject> doWork = (nonDepObj) =>
{
var dependencyObject = CreateDependencyObject(nonDepObj);
UiComponent.Add(dependencyObject);
// Set up some binding on each dependencyObject and update progress bar
...
};
Func<Task> background = async () =>
{
foreach (var nonDependencyObject in myCollection)
{
if (nonDependencyObject.NeedsToBeAdded())
{
doWork(nonDependencyObject);
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
}
}
};
_initializeTask = background();
}

Is it a correct way of using background worker?

Saw this in some code but didn't make sense to me, So I was wondering if even it is a correct way of utilizing backgreound worker?
it is used like this on Form_Load event:
BackgroundWorker asyncWorker = new BackgroundWorker();
asyncWorker.DoWork += new DoWorkEventHandler(AsynchDoWork);
asyncWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(AsynchCompleted);
asyncWorker.RunWorkerAsync();
// a for each loop that can take some time to populate a combobx on the form
Also, is there a better alternative on achieving the same goal? other than backgroundworker?
I would use the Task Parralell Library for this. A good into tutorial is Task Parallel Library: 1 of n. To update a combo box following the result of some background task you could do something like this
// Get UI scheduler. Use this to update ui thread in continuation.
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
// You can use TPL like this...
List<object> objList = new List<object>();
Task<object[]> task = Task.Factory.StartNew<object[]>(() =>
{
// Pull all your data into some object.
object[] objArr = new object[] { "Some", "Stuff", "..." };
return objArr;
}, TaskCreationOptions.PreferFairness);
// Continuation firs after the antecedent task is completed.
task.ContinueWith(ant =>
{
// Get returned data set.
object[] resultSet = (task.Result as object[]);
// Check task status.
switch (task.Status)
{
// Handle any exceptions to prevent UnobservedTaskException.
case TaskStatus.RanToCompletion:
if (task.Result != null)
{
// Update UI comboBox.
}
break;
default:
break;
}
}, CancellationToken.None, TaskContinuationOptions.None, uiScheduler);
Here the uiScheduler lets you update the ui thread from within the continuation delegate which fires when the antecedent task completes (either successfully or otherwise).
The continuation also acts as an exception handler in this case to catch any AggregateExceptions they may be thrown from the background thread.
I hope this helps.
If I understand correctly, what you are actually asking is if you should fill the values in your Combobox from a background thread or from the UI thread.
If the reason it takes a long time to populate the Combobox is because it takes a long time to actually retrieve the items - for example, if you are retrieving them from a database, then you probably should do this work in a BackgroundWorker, and then only add the retrieved items to the Combobox on the RunWorkerCompleted event.
The RunWorkerCompleted event, while still running from a background thread, automatically calls Invoke under the hood for all access to UI elements, and therefore has no problem updating UI elements.
Of course, the long running lop has to be run inside the DoWork event, and not in the same code block as the call to RunWorkerAsync, as in your code example.
Conversely, if we are simply talking about a very large amount of readily available items, and the call to AddRange simply takes a lot of time because of the amount, you can not call this from a background thread, as winforms control can only be accessed from the UI thread.
I'm not sure about the scope, I would create the BackgroundWorker as a member of the class.
The BackgroundWorker takes the work off of the UI thread and has methods that provide easy access to update progress.
As for alternatives, it depends on what exactly your goals are. If it is a small task that doesn't update the UI, then the System.Threading.ThreadPool is an option.
To answer the question "Also, is there a better alternative on achieving the same goal? other than backgroundworker?".
No. This is the easiest way to handle long running blocking tasks for winforms. There are other methods (BeginInvoke), but it is much easier to just use background worker.

Categories

Resources