I have successfully built a plugin mechanism where I can create UI controls in a separate AppDomain and display them as part of a Form in the main AppDomain.
These UI controls do their own data loading so when I open a form about 10 different plugins get created and each needs to load its data.
Again this all works fine if I do it synchronously but I would like to use the async/await pattern in each plugin. My refresh method looks like this:
protected async void RefreshData()
{
_data = await LoadAsync(_taskId); <= UI Thread :)
OnDataChanged(); <= Worker Thread :(
}
Now here starts the problem. When I enter this method I am on the main UI thread. But when the await is over I am on a worker thread so I get a cross thread exception in the OnDataChanged() method which updates the controls.
await should by default use the SynchronizationContext.Current for its continuation but since I am in an other AppDomain this happens to be NULL.
So my question is. How can I configure await to continue on the current thread, that is the UI thread?
I know I can grab a control and do Invoke() but I am also using the MVVM pattern and this is in the View Model so I don't have access to any controls there and all View Model -> View communications are done through data bindings.
I finally figured out how to get back to the UI-Thread from within a separate AppDomain, without having a handle to a control.
Since my view model is always instantiated on the UI thread, I simply grab the current dispatcher:
_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher
Now in my RefreshData method all I have to do is dispatch the action I want to perform after the await.
protected async void RefreshData()
{
_data = await LoadAsync(_taskId); <= UI Thread :)
_dispatcher.Invoke(() => OnDataChanged()); <= UI Thread :)
}
This can of course be made more fancy, by encapsulating the dispatcher, etc.
The idea for this actually came from the: MVVM Light Toolkit
Related
I know there are a lot of questions about this already, but I seem to have a fundamental misunderstanding about how BindingOperations.EnableCollectionSynchronization(observableC, padlock); works.
I have a WPF app using mvvm and in my viewmodel I want to update my observablecollection.
After some googling I landed on this solution that imo should work: Calling it the first time works fine, but after sleeping for 1 minute it gives me this:
System.NotSupportedException: 'This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.'
public MainViewModel()
{
MainOc= new ObservableCollection<DataModel>();
MainView= CollectionViewSource.GetDefaultView(MainOc);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
BindingOperations.EnableCollectionSynchronization(MainOc, padlock);
BindingOperations.EnableCollectionSynchronization(MainView, padlock);
}));
Task.Run(() => GetData());
}
private async void GetData()
{
while (true)
{
lock (padlock)
{
MainOc.Clear();
}
foreach (DataRow row in tempTable.Rows)
{
lock (padlock) {
MainOc.Add(new DataModel());
}
}
lock (padlock)
{
MainView= CollectionViewSource.GetDefaultView(MainOc);
}
Thread.Sleep(TimeSpan.FromMinutes(1));
}
}
I would suspect Application.Current.Dispatcher.BeginInvoke, since this will be run at some later time on the UI thread. Presumably the constructor is already running on the UI thread, so this code will be placed last in the queue of tasks the UI thread is scheduled to perform. The documentation for EnableCollectionSynchronization states
The call must occur on the UI thread.
The call must occur before using the collection on a different thread or before attaching the collection to the ItemsControl, whichever is later.
I'm not confident the later point is fulfilled. I would expect the order to be something like:
MainViewModel() is run
GetData() start updating the collection (on background thread)
The viewmodel is attached to the view
BindingOperations.EnableCollectionSynchronization is called
So I would at least try to call BindingOperations.EnableCollectionSynchronization directly, without the BeginInvoke. Or at the very least, place some breakpoints or logging to verify that the EnableCollectionSynchronization is called before the viewmodel is attached.
In any case I would probably not recommend using that kind of synchronization mechanism. My recommendation would be to:
Use a dispatch timer to invoke a method on the UI thread every minute
If method do any slow operation, like fetching data from the database, make the method async and either do a async database call, or a synchronous database call on a background thread that returns the result. In either case the call should be awaited.
Once you have the data, update your collections to update the UI. This code will be run on the UI thread, even if you awaited work on a background thread, so there is no need for synchronization.
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.
I am working on windows app which generates various results of testing. The UI provides a button Results which has various methods as event handlers. These methods are provided by an API module. The module fetches the data from warehouse and then displays it to the user.
The API call is asynchronous.
But UI gets blocked once the button is clicked.
After quite some readings , I came to know that Dispatcher helps in running a process in background . Dispatcher can be invoked/call only by the UI thread.
How can I implement dispatcher to keep the UI away from being blocked?
The function in c# looks something like this
private async void get_results(object sender, RoutedEventArgs e)
{
List<resultsummary> data = new List<resultsummary>();
if(id==plan_id)
{
data= await getdata.getsummary(id, name);
}
}
Edit:-
This is my understanding of async and await in the above code. But it gives an error cannot await. The getdata class is in differnet namespace and the method getsummary is defined in that space.
The main objective is to unblock the UI and how to go about this using Dispatcher technique?
You don't need to use a Dispatcher to do what you want. In order to fetch data on a background thread and then pass that data from the background thread to the UI thread, you can use the Task class.
Task.Factory.StartNew((Func<YourDataType>)delegate()
{
// Fetch data on background thread here (return YourDataType, whatever that is)
return DataAccessClass.GetData();
}).ContinueWith((Task<YourDataType> task) =>
{
// Update controls with result on UI thread here
YourUiProperty = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
Obviously, you'll need to replace the YourDataType type with whatever your data type is.
You can use Action...
Put your code in an Action like this"
Action action = () =>
{
// your code...
};
assume that you have a label to show result
myLabel.Dispatcher.BeginInvoke(action);
I am developing two versions of the same app for both Windows Store and Windows Phone 8 using the MVVM pattern. Each app has its own View. Model and ViewModel are shared in a Portable Class Libraray. I'm doing asynchronous operations within the Model by using TPL Tasks. Due to the restrictions of the Portable Class Library, I cannot use the async and await keywords.
After a Task has finished I want to come back to the UI thread and update some properties (which will cause the ViewModel and View to update too).
This seems to me like a quite common situation so I'm a little confused why it turns out to be so hard.
I tried two different approaches:
One (does not work)
Save a reference to the scheduler before starting the operation
TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
then pass it to the ContinueWith method.
myTask.ContinueWith(t => myTaskCompleted(t.Result), scheduler);
This seems to me lika a good solution but does not work. myTaskCompleted is still executed in a different thread.
Second
Now I tried to use
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler);
Because I cannot use the Dispatcher from the PCL directly, I pass a reference to it (hidden in a wrapper) to almost every object in the model. (Like in this answer) This finally works but it's quite complicated and ugly.
So my questions are:
Which is the recommended way to get back to the UI thread inside a Portable Class Libraray?
What is my mistake in attempt One?
I know there already are lots of questions to this topic but unfortunately nothing really solved my problem.
TPL will use a thread from the thread pool, and the UI thread is the 'main thread' which isn't on the thread pool and never becomes available to run tasks upon. Using the ContinueWith function will grab another thread from the thread pool to execute your code upon. The core of the problem you are facing is in the fact that Windows Phone does not queue property changes and will make a direct attempt to update the view. Somewhere in your code you should have a Changed function which broadcasts property changes. I'll use mine:
public void Changed(string Key) {
// Check if the property changed has subscribers.
if (PropertyChanged != null) {
// Invoke the property changed.
PropertyChanged(this, new PropertyChangedEventArgs(Key));
}
}
This Changed function will work fine under a WPF application, because WPF will queue property changes and handles them on the next UI frame update. Since Windows Phone does not, we need to establish a pattern to change this behaviour at runtime. I made a property called Dispatcher, which I allow to be set at run-time. All my broadcasts have now been changed from Changed to Dispatcher.
private Action<string> _Dispatcher;
public Action<string> Dispatcher {
get {
if (_Dispatcher == null) {
return Changed;
}
return _Dispatcher;
}
set {
_Dispatcher = value;
}
}
So now we can change the Dispatcher at run-time in our Windows Phone application. We need to write a function that postpones changes until the UI thread is active to broadcast the changes. I did this in an extension so it is a little easier to attach the UI thread safety on a ViewModel. The run-time changes will simply use Windows Phone Dispatcher to scheduele broadcasts on the UI thread. The implementation is as followed:
public static void Attach(this ViewModelStore ViewModelStore, DependencyObject DependencyObject) {
// Set the changed event dispatcher.
ViewModelStore.Dispatcher = (Key) => {
// Begin invoking of an action on the UI dispatcher.
DependencyObject.Dispatcher.BeginInvoke(() => {
// Raise the changed event.
ViewModelStore.Changed(Key);
});
};
}
The ViewModelStore is the generic class I have been using for all my view models, so this function allows me to attach the thread-safe broadcasting mechanism to all of the view models. DependencyObject is a UI-component, such as a view. Now, all you really need to do is call attach on the view model.
ProviderViewModel.Attach(this); // This is inside a Page.
All the broadcasts are not delegated to the UI-thread and invoked the next frame the UI comes in and updates everything accordingly. You won't have to worry about thread safety like this, but you need to remember to Attach a new instance of a view model in your Windows Phone application. Let me know if there are further questions, and good luck!
I'm struggling to get access to the ui thread in an asynchronous httpwebrequest callback in a browser helper object. The current document and windows don't reflect those witnessed on the calling thread, so I can't update the UI as required.
Can anyone help?
Cheers
I'm not certain what context you're in, but in WinForms, you can access the main Form's UI thread from another thread with form.Invoke() like this:
// Assuming the following Form and method:
Form form = ...
Action method = ...
// Invoke the method like this, so it is run in the UI thread.
if (form.InvokeRequired)
{
form.Invoke(method);
}
// If we are already in the UI thread,
// just run the method without an invoke.
else
{
method();
}