Многопоточное приложение WPF: Dispatcher Invoke. Более эффективный способ? - PullRequest
7 голосов
/ 28 марта 2011

Использование .NET 3.5

Привет, ребята, я делаю приложение WPF для проекта, и я просто искал немного понимания Dispatcher и многопоточности. Пример моей программы:

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])));

Я понимаю, что WPF не нравится, когда другой поток обращается к объекту, отличному от того, который его создал. Однако я подумал, что, безусловно, должен быть лучший способ, чем совершать так много вызовов диспетчера, может кто-нибудь, по крайней мере, подтолкнуть меня в правильном направлении (если есть лучшее решение, которое есть).

Ура, Спарки

Ответы [ 4 ]

16 голосов
/ 28 марта 2011

Вы можете начать с того, чтобы быть менее многословным в своих вызовах, т.е.

Application.Current.Dispatcher.Invoke(() =>_aCollection.Add(new Model(aList[i], aSize[i])));

Еще один прием, который мне нравится использовать, заключается в создании быстрого метода, подобного этому:

public static void UiInvoke(Action a)
{
  Application.Current.Dispatcher.Invoke(a);
}

Тогдавам нужно сделать еще меньше, например:

UiInvoke(() =>_aCollection.Add(new Model(aList[i], aSize[i])));

Использование dispatcher.Invoke () - это просто способ вернуть действие обратно в поток пользовательского интерфейса, где, вероятно, и были созданы эти объекты (_aCollection).на первом месте.Если рассматриваемые элементы не имеют прямого взаимодействия с потоком пользовательского интерфейса, то вы можете создавать / манипулировать ими в другом потоке, избавляя от необходимости использовать диспетчер.Конечно, этот подход может стать более сложным в зависимости от того, что вы делаете.

13 голосов
/ 28 марта 2011

Самый простой способ - объединить все три вызова в один:

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])
                      }));
6 голосов
/ 28 марта 2011

ЕСЛИ вы используете .Net 4.0, я бы хотел использовать System.Threading.Tasks .Это кажется ярким примером для продолжений .

2 голосов
/ 29 марта 2011

Ваша проблема связана с тем, что ObservableCollection не отправляет автоматически изменения в поток пользовательского интерфейса. Это отличается от простой формы INotifyPropertyChanged, которая делает это автоматически. Я бы порекомендовал создать свой собственный ObservableCollection, который реализует INotifyCollectionChanged, который автоматически отправляет изменения в поток пользовательского интерфейса.

Вы можете увидеть пример здесь: SynchronizedObservableCollection и BindableCollection

Старый ответ / вопрос: Используете ли вы DependencyObject и DependencyProperties для привязки? Если да, то брось. Это обсуждалось много раз, и это одна из основных причин, по которой вместо него следует использовать INotifyPropertyChanged . Нужно только использовать диспетчер, чтобы изменить свойства самих объектов графического интерфейса и это очевидно из вашего примера, это не то, что вы делаете. А сама привязка автоматически запускается диспетчером.

Также см. Просмотр моделей: POCO против объектов DependencyObjects

...