Multi-threaded WPF Application: Dispatcher Invoke. A more efficient way?
Solution 1
You can start by being less verbose in your calls, i.e.
Application.Current.Dispatcher.Invoke(() =>_aCollection.Add(new Model(aList[i], aSize[i])));
Another trick that I like to use is to make a shortcut method like this:
public static void UiInvoke(Action a)
{
Application.Current.Dispatcher.Invoke(a);
}
Then you have even less to do, as in:
UiInvoke(() =>_aCollection.Add(new Model(aList[i], aSize[i])));
Using dispatcher.Invoke() is really just how you get the action back onto the UI thread, which is probably where these objects (_aCollection) were created in the first place. If the items in question don't have direct interaction with the UI thread, then you can create / manipulate them on a different thread, removing the need to use the dispatcher. Of course this approach could become more complicated depending on what you are doing.
Solution 2
The easiest way would be to combine all three calls into one:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{
_aCollection.Add(new Model(aList[i], aSize[i]);
_Data.Add(new DataPoint<double, double>(Id, aList[i]);
_historical[0].Add(aList[i])
}));
Solution 3
IF you're using .Net 4.0 I would look into using System.Threading.Tasks. This seems like a prime example for continuations.
Solution 4
Your problem stems from fact, that ObservableCollection doesn't automaticaly dispatch changes to UI thread. This is different form simple INotifyPropertyChanged, that does it automaticaly. I would recomend creating your own specific ObservableCollection, that implements INotifyCollectionChanged, that automaticaly dispatches changes to UI thread.
You can see example here: SynchronizedObservableCollection and BindableCollection
Old answer/question: Are you using DependencyObject and DependencyProperties for your binding? If yes, then drop it. It was discussed many times and this is one of the bigger reasons why to use INotifyPropertyChanged instead. Only need to use dispatcher is to modify properties of GUI objects themselves and its obvious from your example, that is not what are you doing. And binding itself is run through dispatcher automaticaly.
Sparky
Updated on January 05, 2021Comments
-
Sparky over 3 years
I am using .NET 3.5 .
I am making a WPF application for a project and I was just looking a bit of insight regarding the Dispatcher and multithreading. An example of my program:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action( () =>_aCollection.Add(new Model(aList[i], aSize[i])))); Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action( () => _Data.Add(new DataPoint<double, double>(Id, aList[i])))); Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action( () => _historical[0].Add(aList[i])));
I understand that WPF does not like when another thread accessing an object other than the one that created it. However, I was thinking there has to surely be a better way than making so many dispatcher invokes, could someone please push me in the right direction at least (if there is a better solution that is).
-
Adam Robinson about 13 yearsTasks won't help if he's already outside the message pump thread (which is where the automatic synchronization would come in handy)
-
CodingGorilla about 13 yearsSure it will, because you can schedule your "Background" tasks on a background thread and then "Finish" it up with a continuation on the UI thread like:
Task.ContinueWith(FinishMyStuff, TaskScheduler.FromCurrentSynchronizationContext)
-
Adam Robinson about 13 yearsSure, if the OP is in a position to move all of the current code into Tasks and place it all on the Window/Control. However, if he's not in a position to rework the entire threading model, then Tasks won't help.
-
CodingGorilla about 13 years@AdamRobinson Well he was asking for "insight" and he didn't offer any restrictions, so I offered up what I thought was the best "idea" I had. He's free to ignore it if he choses.
-
Sparky about 13 yearsHi Guys, Thanks so much for the replies, unfortunately I can't implement them as I'm using .Net 3.5 :) None the less, I'll research the above-mentioned for future reference! Cheers
-
Sparky about 13 yearsI like this idea! Thanks. I have 2 threads running in the one class: 1 for getting data for the database, the other for issuing a RaisePropertyChanged to update the UI. I understand this probably isn't the best way of doing things, to be honest, I'm no expert in WPF so the idea of a UI Thread and backgroundWorkers is a bit foreign to me at the minute
-
Sparky about 13 yearsI didn't know that was possible, learn something new everyday! Cheers.
-
Adam Robinson about 13 years@Sparky: All you're doing with the
=>
operator is creating a lambda expression, which is a type of anonymous delegate. These are all syntactic sugars that the compiler ends up turning into ordinary instance (or sometimes static) methods. You can do anything within a lambda expression (when it's used as an anonymous delegate, as it is here) that you can do within an ordinary function, except for (possibly; I haven't checked)ref
andout
parameters. -
A.R. about 13 yearsNo, that is pretty much how things are done in WPF, so I woukd say that your approach is just fine. I will agree that it can be a pain to do the UI stuff, but a necessary evil I guess. Coding gorilla also make a good point above about the continuations, but those can get into super-slop territory so be advised. Good luck!
-
Sparky about 13 yearsHi Euphoric, cheers, but no, I'm not using Dependency Properties. Also, I'm using MVVM light so currently I use "RaisePropertyChanged" over "INotifyPropertyChanged." Thanks
-
Euphoric about 13 yearsThats weird. So what are _aCollection, _Data and _historical in your example? If they are not part of GUI, then there is no need to change them through Dispatcher. And I dont see ANY need to use Dispatcher when using MVVM.
-
Euphoric about 13 yearsI dont think this is "pretty much how things are done in WPF". Databinding should do dispatching automaticaly. Especialy if he is using MVVM. See my updated answer.
-
A.R. about 13 years@Euphoric: That is all well and good for bindings, but that is clearly not the case here. Also, you can't make assumptions about the more complex interactions of his code. So you are pretty much left to do the dispatching yourself.
-
Sparky about 13 yearsHmmm... Well in that case how would you get around the problem where unnamed threads are accessing an Observable Collection? _Data are chart points for Visiblox and _aCollection is an obervable collection binded by a listview
-
digz6666 over 9 yearsIt worked for me, got rid of print exception: The calling thread cannot access this object because a different thread owns it., stacktrace: at System.Windows.Threading.Dispatcher.VerifyAccess()