Universal Windows UI Responsiveness with Async/Await - c#

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.

Related

Why would await Task.Run() not return to the same thread?

I'm running into a situation that's making me think I don't understand async / await mechanics as well as I thought.
I've got a Windows desktop app which is mostly WPF but uses a WinForms host to show a 3rd party COM object. The process of loading the COM object is pretty slow, so I've been trying to move the creation and initialization of those objects to a task to free up the UI while that work happens, but I'm finding that in some situations when the await Task.Run() returns, it's not on the UI thread. As a result, when I change the Visible property on the WinForms host after the task returns it throws because of a cross-thread call.
The calling function looks like this:
public async Task<bool> LoadPreview(string filePath)
{
bool result;
try
{
await _semaphore.WaitAsync();
result = await Task.Run(() => CreateAndInitializePreviewer(filePath));
if (result)
{
Visible = false; // <-- occasionally crashes because I'm not on the UI thread
_currentHandler.DoPreview();
Visible = true;
}
}
finally
{
_semaphore.Release();
}
return result;
}
The code inside CreateAndInitializePreviewer does not have any async / await calls. I've verified that before the call to Task.Run() I'm always on the UI thread.
Any suggestions on what I should be looking for that would cause the await Task.Run() to come back to a different thread? Any ideas are appreciated.
The process of loading the COM object is pretty slow, so I've been trying to move the creation and initialization of those objects to a task to free up the UI while that work happens
This is probably not going to work, unless your COM object is free-threaded (which is unlikely). If you want to push that work off your UI thread, you'll probably need to create a separate STA thread to hold the COM object and marshal calls to/from that thread - meaning all calls to that COM object, since it would live in the other STA thread. It's fairly straightforward in WPF to create a second UI (STA) thread; it's quite a bit harder but still possible in WinForms.
Any suggestions on what I should be looking for that would cause the await Task.Run() to come back to a different thread?
Yes. await will capture SynchronizationContext.Current if it is not null, and it should not be null in this case. It should be either an instance of DispatcherSynchronizationContext (which continues executing by sending a message to the WPF dispatcher) or WinFormsSynchronizationContext (which continues executing by sending a message to the WinForms winproc).
My initial guess is that there's something odd going on with SynchronizationContext.Current due to the WinForms-in-WPF architecture.

C# Thread.Sleep()

I need somehow to bypass Thread.Sleep() method and don't get my UI Thread blocked, but I don't have to delete the method.
I need to solve the problem without deleting the Sleep method. The Sleep method simulates a delay(unresponsive application). I need to handle that.
An application is considered non-responsive when it doesn't pump its message queue. The message queue in Winforms is pumped on the GUI thread. Therefore, to make your application "responsive", you need to make sure the GUI thread has opportunities to pump the message queue - in other words, it must not run your code.
You mentioned that the Thread.Sleep simulates a "delay" in some operation you're making. However, you need to consider two main causes of such "delays":
An I/O request waiting for completion (reading a file, querying a database, sending an HTTP request...)
CPU work
The two have different solutions. If you're dealing with I/O, the best way would usually be to switch over to using asynchronous I/O. This is a breeze with await:
var response = await new HttpClient().GetAsync("http://www.google.com/");
This ensures that your GUI thread can do its job while your request is pending, and your code will restore back on the UI thread after the response gets back.
The second one is mainly solved with multi-threading. You should be extra careful when using multi-threading, because it adds in many complexities you don't get in a single-threaded model. The simplest way of treating multi-threading properly is by ensuring that you're not accessing any shared state - that's where synchronization becomes necessary. Again, with await, this is a breeze:
var someData = "Very important data";
var result = await Task.Run(() => RunComplexComputation(someData));
Again, the computation will run outside of your UI thread, but as soon as its completed and the GUI thread is idle again, your code execution will resume back on the UI thread, with the proper result.
something like that maybe ?
public async void Sleep(int milliseconds)
{
// your code
await Task.Delay(milliseconds); // non-blocking sleep
// your code
}
And if, for reasons that escape me, you HAVE to use Thread.Sleep, you can handle it like that :
public async void YourMethod()
{
// your code
await Task.Run(() => Thread.Sleep(1000)); // non-blocking sleep using Thread.Sleep
// your code
}
Use MultiThreading.
Use a different thread for sleep rather than the main GUI thread. This way it will not interfere with your Main application

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();
}

WinRT synchronising async calls across threads

I'm having an issue with a WinRT project. Currently the execution on the program is running on two threads. One thread executes the main application and the other handles the UI side of things. At the moment, I'm having an issue calling a function from the main thread to execute on the UI thread, waiting for a reply and then continuing execution on the main thread... let me show you some code as an example.
public async void SignOut(Action onSuccess, Action onFailure)
{
bool success = false;
bool wait = true;
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await SignOutAsync();
success = true;
wait = false;
});
while (wait) { }
if (success)
{
onSuccess();
}
else
{
onFailure();
}
}
So this code is doing what I want it to do but it's obviously not the right way to go about it with the busy waiting and all of that. The problem is that if I move the OnSuccess/OnFailure execution into the RunAsync lambda then there is an error on the callback about invalid memory because the execution is on a different thread. Currently the problem I'm facing is that I can't remove the busy wait without screwing up the order of execution. Ideally I want to wait for the entire RunAsync lambda to finish execution on the UI thread and then return to the main thread to run the success/fail callbacks.
It appears at the moment that as soon as I hit the await SignOutAsync() part of the RunAsync lambda the RunAsync task marks itself as complete and returns to the Success/Failure check before the SignOutAsync method has any result. I believe this is due to the nested async methods and that you can't really await on the RunAsync call and then again on the async lambda within it.
Any advice would be greatly appreciated.
Currently the execution on the program is running on two threads. One thread executes the main application and the other handles the UI side of things.
This is not ideal. If at all possible, structure your code so that you only have one "special" thread (the UI thread). async allows your UI thread to remain responsive without requiring a second "special" thread.
At the moment, I'm having an issue calling a function from the main thread to execute on the UI thread, waiting for a reply and then continuing execution on the main thread.
Again, a better design is to have your program logic provide "services" to the UI, instead of the other way around. So, do your best to redesign the calls so that the UI is driving the program logic and not the opposite.
That said, if you absolutely must have a single "special" background thread, you can use the AsyncContextThread type from my AsyncEx library. AsyncContextThread understands asynchronous methods, so you can do this:
public async Task SignOutAsync(Action onSuccess, Action onFailure)
{
try
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => SignOutAsync());
onSuccess();
}
catch
{
onFailure();
}
}
However, I would be embarrassed to put this code into production; anything that uses Dispatcher is a code smell. Even though I wrote the AsyncContextThread type, I can't recommend it for Windows Store projects. A far better design is to structure the code so that the program logic never calls back into the UI.

How do I make my application wait for my UI to refresh?

Without using extra threads I would simply like to display a "Loading" label or something similar to the user when a large amount of data is being read or written. If I however attempt to modify any UI elements before calling the IO method, the application freezes for a while and then displays the "Loading" message after all the work is already done. This obviously doesn't help. How can I ensure that any UI changes are applied and visible before calling the IO method?
DataSet ds = STT_Import.ImportExcelToDataSet(filePath);
bool result = false;
if (ds != null)
{
int cellCount = ds.GetTotalCellCount();
if (Popup.ShowMessage(string.Format("Your file contains {0} cells. Inserting data will take approximately {1} seconds. Do you want to continue?",
cellCount, CalculateTime(cellCount)), "Confirm", MessageType.Confirm) == MessageBoxResult.Yes)
{
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
// Do actual work after user has been notified:
result = DB.StoreItems(_currentType, ds);
}
}
I tried looking for answers but couldn't find anything that answered my specific question, so I'm sorry if the question has been asked before.
When working with WPF, you can use the Dispatcher to queue commands on the UI thread at different DispatcherPriorities
This will allow you to queue your long-running process on the UI thread after everything in the DispatcherPriority.Render or DispatcherPriority.Loaded queues have occurred.
For example, your code may look like this:
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
// Do actual work after user has been notified:
Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(delegate() {
var result = DB.StoreItems(_currentType, ds); // Do Work
if (result)
StatusLabel.Content = "Finished";
else
StatusLabel.Content = "An error has occured";
}));
It should be noted though that its usually considered bad design to lock up an application while something is running.
A better solution would be to run the long-running process on a background thread, and simply disable your application form while it runs. There are many ways of doing this, but my personal preference is using the Task Parallel Library for it's simplicity.
As an example, your code to use a background thread would look something like this:
using System.Threading.Tasks;
...
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
MyWindow.IsEnabled = False;
// Do actual work after user has been notified:
Task.Factory.StartNew(() => DB.StoreItems(_currentType, ds))
// This runs after background thread is finished executing
.ContinueWith((e) =>
{
var isSuccessful = e.Result;
if (isSuccessful)
StatusLabel.Content = "Finished";
else
StatusLabel.Content = "An error has occured";
MyWindow.Enabled = true;
});
You are trying to solve the problem in the wrong manner. What you should be doing here is run the time-consuming task in a worker thread; this way, your UI will remain responsive and the current question will become moot.
There are several ways you can offload the task to a worker thread; among the most convenient are using the thread pool and asynchronous programming.
It is provably impossible to keep your UI responsive without utilizing additional threads unless your database provides an asynchronous version of the method you're using. If it does provide an asynchronous version of the method then you simply need to use that. (Keep in mind that async does not mean that it's using any other threads. It's entirely possible to create an asynchronous method that never uses additional threads, and that's exactly what's done with most network IO methods.) The specifics of how to go about doing that will depends on the type of DB framework you're using, and how you're using it.
If your DB framework does not provide async methods then the only way to keep the UI responsive is to perform the long running operation(s) in a non-UI thread.
The Approach you are using is not efficient way so I would suggest to go with Async Programing or threading
Async programming:
Visual Studio 2012 introduces a simplified approach, async programming, that leverages asynchronous support in the .NET Framework 4.5 and the Windows Runtime. The compiler does the difficult work that the developer used to do, and your application retains a logical structure that resembles synchronous code. As a result, you get all the advantages of asynchronous programming with a fraction of the effort. Support .Net framework 4.5
It will save your time to implementing System .Threading and very efficient for the task same as your where we have to wait for some operation
http://msdn.microsoft.com/en-ca/library/vstudio/hh191443.aspx
http://go.microsoft.com/fwlink/?LinkID=261549
or
Threading:
The advantage of threading is the ability to create applications that use more than one thread of execution. For example, a process can have a user interface thread that manages interactions with the user and worker threads that perform other tasks while the user interface thread waits for user input.Support .Net fremework 4.0 or Older
http://msdn.microsoft.com/en-us/library/aa645740%28v=vs.71%29.aspx
If you don't want the UI to be responsive I use a busy indicator.
There are prettier cursors - this is an in house application.
using (new WaitCursor())
{
// very long task
Search.ExecuteSearch(enumSrchType.NextPage);
}
public class WaitCursor : IDisposable
{
private Cursor _previousCursor;
public WaitCursor()
{
_previousCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
}
#region IDisposable Members
public void Dispose()
{
Mouse.OverrideCursor = _previousCursor;
}
#endregion
}

Categories

Resources