WPF / C中的线程和集合修改#

我正在开发一个C#/ WPF系统,它访问一个SQL数据库,检索一些数据(大约10000个项目),然后应该更新一个数据点集合,用作我在我的应用程序中使用的WPF图表的数据(Visifire图表解决方案,万一有人想知道)。

当我编写直接的单线程解决方案时,系统会像您预期的那样挂起应用程序查询数据库,检索数据和呈现图表所花费的时间。 但是,我想通过在使用multithreading获取和处理数据时向用户添加等待动画来更快地完成此任务。 但是,出现了两个问题:

  1. 在使用multithreading时,我无法更新我的集合并使它们保持同步。 我对Dispatcher课程不太熟悉,所以我不太清楚该怎么做。
  2. 由于我显然没有很好地处理multithreading,因此等待动画不会显示(因为UI被冻结)。

我想弄清楚是否有一种很好的方法可以有效地使用multithreading进行收集。 我发现微软有线程安全的collections,但似乎没有一个符合我的需求。

此外,如果有人有一个很好的参考来学习和理解Dispatcher我会非常感激。

编辑:这是我正在尝试做的代码片段,也许它可以解释我的问题:

 private List InitializeDataSeries(RecentlyPrintedItemViewModel item) { var localDataPoints = new List(); // Stopping condition for recursion - if we've hit a childless (roll) item if (item.Children.Count == 0) { // Populate DataPoints and return it as one DataSeries _dataPoints.AddRange(InitializeDataPoints(item)); } else { // Iterate through all children and activate this function on them (recursion) var datapointsCollection = new List(); Parallel.ForEach(item.Children, child => datapointsCollection = (InitializeDataSeries((RecentlyPrintedItemViewModel)child))); foreach (var child in item.Children) { localDataPoints.AddRange(InitializeDataSeries((RecentlyPrintedItemViewModel)child)); } } RaisePropertyChanged("DataPoints"); AreDataPointsInitialized = true; return localDataPoints; } 

谢谢

Dispatcher是一个用于在单个线程上管理多个工作项队列的对象,每个队列在执行它的工作项时具有不同的优先级。

Dispatcher通常引用WPF的主应用程序线程,用于在不同的DispatcherPriorities中调度代码,以便它们按特定顺序运行。

例如,假设您要显示加载图形,加载一些数据,然后隐藏图形。

 IsLoading = true; LoadData(); IsLoading = false; 

如果你一次完成这一切,它将锁定你的应用程序,你将永远不会看到加载图形。 这是因为所有代码默认都在DispatcherPriority.Normal队列中运行,所以当它完成运行时,加载图形将再次被隐藏。

相反,您可以使用Dispatcher加载数据并以比DispatcherPriority.Render更低的调度程序优先级隐藏图形,例如DispatcherPriority.Background ,以便在加载发生之前完成其他队列中的所有任务,包括渲染加载图形。

 IsLoading = true; Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate() { LoadData(); IsLoading = false; })); 

但这仍然不理想,因为Dispatcher引用了应用程序的单个UI线程,因此在长时间运行的进程发生时,您仍然会锁定线程。

更好的解决方案是为长时间运行的过程使用单独的线程。 我个人的偏好是使用任务并行库,因为它简单易用。

 IsLoading = true; Task.Factory.StartNew(() => { LoadData(); IsLoading = false; }); 

但是这仍然会给你带来问题,因为WPF对象只能从创建它们的线程中修改。

因此,如果在后台线程上创建ObservableCollection ,则无法在代码中除该后台线程之外的任何位置修改该集合。

典型的解决方案是在后台线程上获取数据并将其返回到temp变量中的主线程,并让主UI线程创建对象并使用从后台线程获取的数据填充它。

所以你的代码经常看起来像这样:

 IsLoading = true; Task.Factory.StartNew(() => { // run long process and return results in temp variable return LoadData(); }) .ContinueWith((t) => { // this block runs once the background code finishes // update with results from temp variable UpdateData(t.Result) // reset loading flag IsLoading = false; });