2011-03-28 81 views
6

使用.NET 3.5多线程WPF应用程序:调度程序调用。更有效的方法?

嗨,大家好,我正在做一个项目一个WPF应用程序,我只是在寻找有关调度和多线程的一些真知灼见。我的程序的一个例子:

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不喜欢另一个线程访问除创建它之外的其他线程。但是,我认为必须有更好的方式比调用这么多的调度程序更好,至少有人能让我朝正确的方向发展(如果有更好的解决方案的话)。

干杯, 斯帕克

回答

14

您可以通过为您的通话更简洁开始,即

​​

另一个技巧,我喜欢用就是建立一个快捷方式的方法是这样的:

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

然后,你必须连少做,如:

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

使用dispatcher.Invoke()实际上只是你如何得到行动回来到UI线程,这可能是首先创建这些对象(_aCollection)的地方。如果有问题的项目与UI线程没有直接交互,那么您可以在不同的线程上创建/操作它们,从而无需使用调度程序。当然这种方法可能会变得更复杂,取决于你在做什么。

+0

我喜欢这个主意!谢谢。我有两个线程在一个类中运行:1为数据库获取数据,另一个用于发布RaisePropertyChanged以更新UI。我明白这可能不是最好的做事方式,说实话,我不是WPF的专家,所以UI线程和backgroundWorkers这个想法对我来说有点外国, – Sparky 2011-03-28 20:09:16

+1

不,这是非常多的WPF中的事情是如何完成的,所以我会说你的方法很好。我会同意做UI的工作可能会很痛苦,但我猜想是一个必要的罪恶。编码大猩猩在延续上面也提供了一个很好的观点,但那些可以进入超级领域,因此建议。祝你好运! – 2011-03-28 20:51:01

+0

我不认为这是“在WPF中完成的事情”。数据绑定应该自动进行调度。特别是如果他使用MVVM。看到我更新的答案。 – Euphoric 2011-03-29 09:30:49

12

最简单的方法可能是将所有三个电话合并为一个:

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]) 
         })); 
+0

我不知道这是可能的,学到新的东西每天!干杯。 – Sparky 2011-03-28 20:09:41

+1

@Sparky:你用'=>'操作符所做的全部工作就是创建一个lambda表达式,这是一种匿名委托。这些都是编译器最终变成普通实例(或者有时是静态的)方法的所有语法糖。你可以在lambda表达式中做任何事情(当它用作匿名委托时,就像它在这里一样),除了(可能的;我没有检查过)'ref'和'out'参数。 – 2011-03-28 20:19:17

+0

它为我工作,摆脱了打印异常:调用线程无法访问此对象,因为一个不同的线程拥有它。,stacktrace:在System.Windows.Threading.Dispatcher.VerifyAccess() – digz6666 2014-11-21 11:56:12

6

如果你使用.NET 4.0我会考虑使用System.Threading.Tasks。这似乎是continuations的一个主要例子。

+1

任务不会帮助,如果他已经在消息泵线程之外(这是自动同步会派上用场的地方) – 2011-03-28 19:38:21

+5

当然会,因为你可以在后台线程上安排你的“后台”任务,然后在UI线程上继续“完成”它,就像:'Task.ContinueWith(FinishMyStuff,TaskScheduler.FromCurrentSynchronizationContext)' – CodingGorilla 2011-03-28 19:44:49

+1

当然,如果OP能够将所有当前代码移动到任务中,并将其全部放在窗口/控件上。但是,如果他无法重做整个线程模型,那么Tasks将无济于事。 – 2011-03-28 19:55:48

2

您的问题源于事实,即ObservableCollection不会自动将更改发送到UI线程。这与简单的INotifyPropertyChanged不同,它会自动执行。我会推荐创建自己的特定的ObservableCollection,实现INotifyCollectionChanged,它会自动调度UI线程的更改。

你可以看到这里的例子: 你使用你的绑定DependencyObjectDependencyPropertiesSynchronizedObservableCollection and BindableCollection

旧答案/问题?如果是的话,就放弃它。讨论过很多次,这是为什么要用INotifyPropertyChanged代替的一个重要原因。只需要使用调度程序就是修改GUI对象本身的属性,在你的例子中显而易见,那不是你在做什么。绑定本身是通过调度程序自动运行的。

另见View Models: POCOs versus DependencyObjects

+0

嗨Euphoric,欢呼,但不,我没有使用依赖属性。另外,我正在使用MVVM light,所以目前我在“INotifyPropertyChanged”上使用“RaisePropertyChanged”。谢谢 – Sparky 2011-03-29 08:46:55

+0

这很奇怪。那么在你的例子中什么是_aCollection,_Data和_historical?如果它们不是GUI的一部分,那么就不需要通过Dispatcher来更改它们。我没有看到使用MVVM时需要使用Dispatcher。 – Euphoric 2011-03-29 09:11:29

+0

嗯......那么在那种情况下,如何解决未命名线程访问Observable Collection的问题? _Data是Visiblox的图表点,_aCollection是一个由列表视图绑定的可选集合 – Sparky 2011-04-05 14:55:24