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

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

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.

Run an async loop indefinitely from button click from Xamarin

What is the best way to run a loop infinitely in parallel with the application?
This is what I have tried so far:
The button that actives the loop:
private void ActiveDeactiveTest(object sender, EventArgs e)
{
active = !active;
}
The loop:
bool runTest = false;
bool active = false;
public async void Test()
{
while (runTest)
{
if (active)
{
LblOutput.Text = "before";
await Task.Delay(1000);
LblOutput.Text = "after";
}
else
{
LblOutput.Text = "Idle";
}
}
}
And the form instantiation:
public MainPage()
{
InitializeComponent();
runTest = true;
Test();
}
Fairly new to this so any help would be appreciated
Thanks in advance
I don't think there is the best way to loop infinitely but I will provide some of the options. Idea is to start another thread from your main application thread and leverage the loops, usually while, to manage indefinite looping.
Be careful if you are developing UI application because they are quite sensitive in terms of background work. For example, WPF allow only access to UI elements only to the thread which create them, thread known as Dispatcher. This mean that every background thread which need to update UI elements needs to delegate work to dispatcher thread. This is also the case with android with the difference that Dispatcher thread is called UI thread (WPF Dispatcher Thread, Android UI Thread)
WPF
In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous. The operation is added to the queue of the Dispatcher at the specified DispatcherPriority.
Android
The Android UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread
I will provide few examples just as a show case:
//Option A, common one, loop doesn't need to have iteration at all
Task.Factory.StartNew(() =>
{
while (shouldLooping)
{
//do your job
}
});
//Option B, kind of wierd, loop will have at least one iteration
Task.Factory.StartNew(() =>
{
do
{
//do your job
} while (shouldLooping);
});
//Option C, if you are driven by producer/consumer pattern, BlockingCollection should be shared between producer and consumer
Task.Factory.StartNew(() =>
{
foreach (var item in blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
});

Using Task with Parallel.Foreach in .NET 4.0

I started off trying to add a progress bar to the windows form that updates the progress of code running within a Parallel.Foreach loop. In order to do this the UI thread has to be available to update the progress bar. I used a Task to run the Parallel.Foreach loop to allow the UI thread to update the progress bar.
The work done within the Parallel.Foreach loop is rather intensive. After running the executables of the program(not debugging within visual studio) with the Task, the program became unresponsive. This is not the case if I run my program without Task. The key difference I noticed between the two instances is that the program takes ~80% of the cpu when ran without Task, and ~5% when ran with Task.
private void btnGenerate_Click(object sender, EventArgs e)
{
var list = GenerateList();
int value = 0;
var progressLock = new object ();
progressBar1.Maximum = list.Count();
Task t = new Task(() => Parallel.ForEach (list, item =>
{
DoWork ();
lock (progressLock)
{
value += 1;
}
}));
t.Start();
while (!t.IsCompleted)
{
progressBar1.Value = value;
Thread.Sleep (100);
}
}
Side Note: I know that
Interlocked.Increment(ref int___);
works in place of the lock. Is it considered more efficient?
My Question is three fold:
1.) Why would the program with the Task become unresponsive when the load is much less?
2.) Does using Task to run Parallel.Foreach limit the thread pool of the Parallel.Foreach to only the thread running the task?
3.) Is there a way to make the UI thread responsive instead of sleeping for the .1 second duration without using cancellation token?
I'm grateful for any help or ideas, I've spent quite a lot of time researching this. I also apologize if I've violated any posting format or rules. I tried to adhere to them, but may have missed something.
You can greatly simplify your code there by using the built in Invoke method that invokes a delegate on the owning Windows synchronization context.
From MSDN:
Executes the specified delegate on the thread that owns the control's underlying window handle.
The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string[] GenerateList() => new string[500];
void DoWork()
{
Thread.Sleep(50);
}
private void button1_Click(object sender, EventArgs e)
{
var list = GenerateList();
progressBar1.Maximum = list.Length;
Task.Run(() => Parallel.ForEach(list, item =>
{
DoWork();
// Update the progress bar on the Synchronization Context that owns this Form.
this.Invoke(new Action(() => this.progressBar1.Value++));
}));
}
}
This will invoke the Action delegate on the same UI thread that the Form belongs to, from within the Task.
Now to try and answer your questions
1.) Why would the program with the Task become unresponsive when the load is much less?
I'm not 100% sure, but this could be related to you locking a member on the UI thread. If the load is less, then the lock will happen more frequently, potentially causing the UI thread to "hang" while the progressbar is incremented.
You are also running a while loop that is sleeping the UI thread every 100 milliseconds. You'll see UI hanging due to that while loop.
2.) Does using Task to run Parallel.Foreach limit the thread pool of the Parallel.Foreach to only the thread running the task?
It does not. Several tasks will get created within the Parallel.ForEach call. The underlying ForEach uses a partitioner to spread the work out, and not create more tasks than what is necessary. It creates tasks in batches, and processes the batches.
3.) Is there a way to make the UI thread responsive instead of sleeping for the .1 second duration without using cancellation token?
I was able to handle that by removing the while loop and using the Invoke method to just go ahead and execute a lambda on the UI thread directly.

Update UI while Task.Run or TaskFactory.StartNew is still working

So I have decided to rewrite my Mail client in WPF as I think it's about time I move on from Windows Forms (I still like it), but I am facing a bit of a problem.
I use a BackgroundWorker in my Windows Forms app to do stuff and in a foreach I worker.ReportProgress(currentProgress); and this allows me to update the UI as things are being done in the background which is great.
But just now after starting a new WPF project, I notice that there is no BackgroundWorker in the Toolbox (for WPF apps) so I go searching online, and found that some people have problems updating the UI while using BackgroundWorker with WPF. So this makes me think that using BackgroundWorker in a WPF app is a bit hacky - and I don't want that.
On that same page, another user refers them to this page, telling them to use Task.Run instead of BackgroundWorker in WPF. Upon looking at Task.Run docs, I immediately see how it can be useful, however I do have one concern. I do not see a way to "Report Progress" or to update the UI as things are being done. All I see is how to Run a Task and "await" it; leaving me with just one option - update the UI after the long-running Task has completed.
How can we update the UI of a WPF desktop app while Task.Run/TaskFactory.StartNew is still working?
You can stick with BackroundWorker if you so choose. There is nothing really hacky about it although it is very old-school. As others said, if you can't find it in your toolbox, you can always declare and initialise it straight from your code (don't forget the using System.ComponentModel; directive).
Stephen Cleary has an excellent series of blog posts on BackgroundWorker vs Task, which highlights the differences and limitations of each approach. It's definitely worth a read if you're on the fence or just curious.
http://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-intro.html
If you do decide to go down the Task + async/await route, there are a couple of things specifically related to progress reporting that you should keep in mind.
Generally you should be aiming to have your await Task.Run encapsulate the smallest meaningful amount of work possible. The rest of your async method will then execute on the dispatcher SynchronizationContext (assuming that it was started on the dispatcher thread) and will be able to update the UI directly, like so:
List<object> items = GetItemsToProcess();
int doneSoFar = 0;
foreach (var item in items)
{
await Task.Run(() => SomeCpuIntensiveWorkAsync(item));
doneSoFar++;
int progressPercentage = (int)((double)doneSoFar / items.Count * 100);
// Update the UI.
this.ProgressBar.Value = progressPercentage;
}
This is the easiest way of implementing progress reporting in the async world.
The only time I can imagine reporting the progress from within the body of the delegate you pass to Task.Run is when you're processing a very large number of items, and the processing of each item takes a very short time (we're talking 10,000 items per second as a rough guide). In such a scenario creating a large number of extremely fine-grained Tasks and awaiting them will introduce significant overhead. If this is your case you can fall back to the progress reporting mechanism introduced in .NET 4: Progress<T>/IProgress<T>. It's quite similar to the way the BackgroundWorker reports progress (in that it relies on events) and it provides a bit more flexibility in terms of deciding when you get to post back to the dispatcher context.
public async Task DoWorkAsync()
{
// Let's assume we're on the UI thread now.
// Dummy up some items to process.
List<object> items = GetItemsToProcess();
// Wire up progress reporting.
// Creating a new instance of Progress
// will capture the SynchronizationContext
// any any calls to IProgress.Report
// will be posted to that context.
Progress<int> progress = new Progress<int>();
progress.ProgressChanged += (sender, progressPercentage) =>
{
// This callback will run on the thread which
// created the Progress<int> instance.
// You can update your UI here.
this.ProgressBar.Value = progressPercentage;
};
await Task.Run(() => this.LongRunningCpuBoundOperation(items, progress));
}
private void LongRunningCpuBoundOperation(List<object> items, IProgress<int> progress)
{
int doneSoFar = 0;
int lastReportedProgress = -1;
foreach (var item in items)
{
// Process item.
Thread.Sleep(1);
// Calculate and report progress.
doneSoFar++;
var progressPercentage = (int)((double)doneSoFar / items.Count * 100);
// Only post back to the dispatcher SynchronizationContext
// if the progress percentage actually changed.
if (progressPercentage != lastReportedProgress)
{
// Note that progress is IProgress<int>,
// not Progress<int>. This is important
// because Progress<int> implements
// IProgress<int>.Report explicitly.
progress.Report(progressPercentage);
lastReportedProgress = progressPercentage;
}
}
}

Implement a method with abort ability

How can I implement a method with abort ability?
Imagine I have a grid, when data loads the grid starts calculating the totals and show them on another grid beneath itself. I want to start calculating totals right after loading data.
I tried using threads but a problem rose up:
When I click on Load button two rapid times, for the first time the thread starts working and the second time I get an error saying the thread is busy.
I need to implement some method that can be aborted and started again.
For this I would indeed use threads. Depending on your .NET framework you can start by setting up an IProgress object which can be used to provides UI updates from a background thread-pool thread. So firstly you could create
IProgress<object> progressIndicator =
new Progress<object>(ReportProgress);
where ReportProgress is some method
public void ReportProgress(object o) { /* Update UI components here on the Main UI thread */ }
Now, to start the work and provide cancellation support, you can do some thing like
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
Task task = Task.Factory.StartNew(() =>
{
// Do your work here and provide cancellation support.
for (int i = 0; i < someMaxValue; ++i)
{
// Update the UI if required.
progressIndicator.ReportProgress(someObject);
// No check for you cancellation condition...
if (source != null && someCancelCondition)
source.Cancel(); // This will throw a CancellationException().
}
}, token,
TaskCreationOptions.None,
TaskScheduler.Default)
.ContinueWith(ant =>
{
// Here you must handle you exceptions and trap your cancellation.
});
For more information see J. Albahari's Threading tutorial.
I hope this helps.
When load is clicked, create a CancellationTokenSource and store it. Pass in its CancellationToken to your worker thread, and check it regularly in your calculations so you can abort in a timely manner.
When Load is clicked again, you can abort the current thread by calling Cancel() on your TokenSource.

Categories

Resources