Dispatcher.Yield priority issue - c#

I want to update the position of certain UI elements in my WPF application in a loop. After each iteration, the UI should be rerendered to make the changes visible. The updating process can stopped at any point using a CancellationToken. Since the cancellation is performed by the user, the UI must remain responsive to input. I wrote the following method to do this:
public async Task DoStuff(CancellationToken token)
{
do
{
DoLayoutUpdate();
await Dispatcher.Yield(DispatcherPriority.Input);
} while (!token.IsCancellationRequested);
}
This mostly works: The UI is rerendered after each iteration and I can click the button to cancel the operation so input works as well. The problem is: if there is no input and nothing to rerender, the method gets stuck in the Yield. Presumably the thread is blocked waiting for input or render tasks.
If I increase the DispatcherPriority to Render, the method does not get stuck anymore but then the UI isn't updated and input isn't handled anymore.
How can I fix this?

Try await Task.Delay(10); or await Dispatcher.BeginInvoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.Input); instead of Dispatcher.Yield.
This should give the UI thread a chance to render while your loop executes.

If I increase the DispatcherPriority to Render, the method does not get stuck anymore but then the UI isn't updated and input isn't handled anymore.
Actually, the problem is that you changed the priority in the wrong direction. Setting the priority to DispatcherPriority.Background would allow WPF to finish its work and then eventually schedule the continuation to allow the method to resume execution after the await.
I.e.:
public async Task DoStuff(CancellationToken token)
{
do
{
DoLayoutUpdate();
await Dispatcher.Yield(DispatcherPriority.Background);
} while (!token.IsCancellationRequested);
}
Using a higher priority causes your continuation to be scheduled too soon, giving your loop all the dispatcher time, in preference over all the other things WPF needs to do.
Noting, of course, that calling Dispatcher.Yield() without a parameter will default to using DispatcherPriority.Background as well. Either way works fine.
The other ideas suggested in the accepted answer will work too, but they are a bit kludgy as compared to simply yielding with the correct requested continuation priority.

Or the bizarre monstrosity that works no matter what thread you're running on:
await Task.Run(() =>
{
Action action = () => { };
MainWindow.Dispatcher.Invoke(action,
System.Windows.Threading.DispatcherPriority.Background);
});
Dispatcher.Yield() works fine on the UI thread. But it is a static method that operates on Dispatcher.CurrentDispatcher, and there's no equivalent non-static member.

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.

WPF: how to make async call in UI thread

I have a button in xaml whose mouseclick is connected to a delegate command. In delegate command, I am calling another method which creates bit map and saves it to a pdf. This method takes few seconds(usually more than 10 seconds) during which the app goes to Not responding state where the UI is not accessible. During which time I wanted to show some animation which is already defined in xaml. But showing it just before calling the time-taking-function and hiding it afterwards doesnt work. It shows up at the end of time-taking-function execution if i dont use different thread.
So, I tried using creating a different thread and calling this time taking function. Animation loads fine But the function actually needs to access some UI objects in UI thread. It results in this exception:
"The calling thread cannot access this object because a different thread owns it."
Is there a way to achieve it ? i.e., to show the animation during this function execution ?
Some code snippet:
private void OnExportPDFCommand()
{
PanelLoading = true; // Flag to show animation
Task.Factory.StartNew(() =>
{
CreatePDF(); //time-taking-function
Application.Current.Dispatcher.Invoke(() => PanelLoading = false);
});
}
Please explain with a small example. I am new to WPF. Thanks.
You can make OnExportPDFCommand an async method and await the Task you start:
private async void OnExportPDFCommand()
{
PanelLoading = true; // Flag to show animation
await Task.Run(() =>
{
CreatePDF(); //time-taking-function
});
PanelLoading = false; // stop animation
}
The compiler turns this into a state machine. The control flow is returned to the caller (the UI) at the await keyword. When the Task finishes, execution is eventually resumed at the next statement PanelLoading = false.
This way, the call to PanelLoading is in the UI thread again and no exception should be raised.
If you need to make other calls to the UI (inside CreatePDF) you can use Application.Current.Dispatcher.Invoke(...) to invoke these calls on the UI thread.
As a side note: you might want to disable the button that starts this at the beginning of OnExportPDFCommand() and enable it again at the end. This way you can avoid to start the operation multiple times and get confused with your animation.

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.

AsyncAwait causes unexpected method behavior, Methods runs in wrong thread if called from constructor

I'm trying to do some time consuming actions without freezing the GUI Thread. The UpdateSomething Method is called in two places once in the constructor of my ViewModel and once on button click (RelayCommand). The Method is executed in a WorkerThread if the method is called via RelayCommand, but runs in the MainThread (GUI) when the method call comes from the constructor.
What causes this strange behavior? I double check a few times via IntelliTrace.
This is the method in question:
private async void UpdateSomething()
{
var item = await Task.Factory.StartNew(() =>this.DoSomething("I should run async"));
this.TestItem = item;
}
I'm using WPF and .Net 4.5
For the case where the Tasks action runs on the main thread, the most likely cause is that the method UpdateSomething is being called from within another Task (a Task that was scheduled to run on the main thread). In that case, the TaskScheduler.Current is the main thread TaskScheduler not the TaskScheduler.Default which queues work to the thread pool.
Task.Factory.StartNew defaults to using TaskScheduler.Current (you can change what it uses by calling the appropriate override), while Task.Run uses TaskScheduler.Default.
The statement that Task.Factory.StartNew doesn't play will with async/await is not correct, it's just that some of the defaults are probably not what you want for the common case (this is part of whyTask.Run was introduced).
See:
http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
Why is TaskScheduler.Current the default TaskScheduler?
I ran into the same problem a while ago. I stuck up a little post about it here:
Today I had a major hair pulling moment. I was using a await/async on a block of code wrapped in a new task. No matter what I did it would just start the Task but then not wait for the result.
After much frustration I worked out that using await Task.Factory.StartNew doesn't play well with await.
In the end I simply changed it to Task.Run what was then worked fine.
Basically you need to use Task.Run instead because await doesn't play nicely with Task.Factory.StartNew
This question was answered by #Rene147 but he deleted his answer. (for whatever reason)
I will briefly rephrase what he had written:
await/asycn doesn't play very well with Task.Factory.StartNew().
Simply replace Task.Factory.StartNew by Task.Run and everything should work:
private async void UpdateSomething()
{
//not working when call comes from constructor
//var item = await Task.Factory.StartNew(() =>this.DoSomething("I should run async"));
//working
var item = await Task.Run(() =>this.DoSomething("I run async!"));
this.TestItem = item;
}
Rene147 also wrote a short blogpost about it.
Would be happy to get more information about this issue, still seems odd to me.

Categories

Resources