Multi-threaded WPF Application: Dispatcher Invoke. A more efficient way?

44,122

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.

Also see View Models: POCOs versus DependencyObjects

Share:
44,122
Sparky
Author by

Sparky

Updated on January 05, 2021

Comments

  • Sparky
    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
    Adam Robinson about 13 years
    Tasks won't help if he's already outside the message pump thread (which is where the automatic synchronization would come in handy)
  • CodingGorilla
    CodingGorilla about 13 years
    Sure 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
    Adam Robinson about 13 years
    Sure, 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
    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
    Sparky about 13 years
    Hi 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
    Sparky about 13 years
    I 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
    Sparky about 13 years
    I didn't know that was possible, learn something new everyday! Cheers.
  • Adam Robinson
    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 and out parameters.
  • A.R.
    A.R. about 13 years
    No, 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
    Sparky about 13 years
    Hi 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
    Euphoric about 13 years
    Thats 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
    Euphoric about 13 years
    I 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.
    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
    Sparky about 13 years
    Hmmm... 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
    digz6666 over 9 years
    It 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()