C# 在新线程上使用ObservableCollection
几天前,由于无法从另一个线程更新ObservableCollection,我创建了一个。 这是线程的解决方案:C# 在新线程上使用ObservableCollection,c#,wpf,multithreading,dispatcher,C#,Wpf,Multithreading,Dispatcher,几天前,由于无法从另一个线程更新ObservableCollection,我创建了一个。 这是线程的解决方案: Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate { TheTVDB theTvdb = new TheTVDB(); foreach (TVSeries tvSeries in theTvdb.SearchSeries("Dexter")) { this.Overview.
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate
{
TheTVDB theTvdb = new TheTVDB();
foreach (TVSeries tvSeries in theTvdb.SearchSeries("Dexter"))
{
this.Overview.Add(tvSeries);
}
}),
DispatcherPriority.Background);
但是,这似乎不是真正的解决方案,因为在执行委托时UI仍然冻结。我的猜测是,上面的代码实际上并没有在另一个线程上运行任何东西,而是将其全部分派到UI线程。
因此,我真正想做的是自己创建一个新线程并进行加载(这发生在theTvdb.SearchSeries()
)。然后,我将迭代结果并将其添加到我的ObservableCollection
中,这必须在UI线程上进行
这种方法听起来正确吗
我想出了下面的方法,我认为可以加载结果并将它们添加到ObervableCollection中,并在我的列表视图中显示它们,而不会冻结UI
Thread thread = new Thread(new ThreadStart(delegate
{
TheTVDB theTvdb = new TheTVDB();
List<TVSeries> dexter = theTvdb.SearchSeries("Dexter");
foreach (TVSeries tvSeries in dexter)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate
{
this.Overview.Add(tvSeries);
}),
DispatcherPriority.Normal);
}
}));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
您的方法是危险的,在很短的时间内将大量作业推送到dispatcher上可能会导致应用程序暂停或冻结。当您的一般方法很好时,您可能需要考虑使用批添加元素到列表中。 另外,您不能使用,因为您现在正在使用当前线程的调度程序。因此,您要求您的线程处理同一线程中的添加,而不是ui线程。您需要从ui线程获取调度程序。例如,可以使用应用程序对象
根据我的经验,我还建议您使用它,它在WPF中比普通线程更好。在应用程序中的任何“繁重”工作中使用多线程的方法,您希望保持响应,这是正确的思考方式,因此您走上了正确的轨道 然而,当您在这里创建和使用其他线程时,仍然过于依赖调度程序。考虑到,在这里使用多线程,您的过程应该如下:
public void HeavyLifting(Action<List<Items>> callback)
{
Task<List<Items>> task = Task.Factory.StartNew(
() =>
{
var myResults = new List<Items>();
// do the heavy stuff.
return myResults;
});
callback.Invoke(task.Result);
}
然后,在任务完成时执行回调时传递方法。请注意,这里是我要求调度员执行工作的地方:
private void HandleHeavyLiftingCompleted(List<Items> results)
{
this._uiDispatcher.BeginInvoke(
new Action(() => { this.MyItems = new ObservableCollection<Items>(results); }));
}
private void handlehavylifting已完成(列出结果)
{
这个.\u uiDispatcher.BeginInvoke(
新操作(()=>{this.MyItems=newobserveCollection(results);});
}
请注意,在本例中,涉及的UI工作是更新我从视图绑定到的ObvservableCollection。在这里的示例中,我使用了一个随机的“Item”对象,它可以是您喜欢的任何对象
我使用的是Cinch,因此依赖于一个服务来获取相关的Dispatcher(您在这里看到的是。\ uiDispatcher)。在您的情况下,您可以使用此处其他问题中提到的方法获得对它的引用
另外,如果您有时间阅读,这里有一些关于WPF线程模型的重要信息。您可以简单地执行以下操作:
Task.Factory.StartNew(() =>
{
var theTvdb = new TheTVDB();
var dexterSeries = theTvdb.SearchSeries("Dexter");
Application.Current.Dispatcher.Invoke(new Action(() =>
{
foreach (var tvSeries in dexterSeries)
{
this.Overview.Add(tvSeries);
}
}));
});
您可能会发现,不管怎样,如果可观察集合包含很多项,并且您的UI很复杂,那么UI还是会冻结—我们在这里讨论的是WPF/SL还是Winforms?目前它实际上并不包含很多元素。我不知道有多少,但不超过10个。我使用的是WPF。你的线程代码看起来还不错——你创建了一个新线程并在上面开始了一个非常简单的操作,你能发布你的XAML代码吗?我假设你的数据库在另一个盒子上,或者你正在本地机器上运行所有的东西?我已经更新了我的问题,以显示用于视图的XAML。我通过API从外部加载数据。当使用从
应用程序.Current.dispatcher
检索的dispatcher而不是dispatcher.Current
时,我将得到以下错误:必须在与DependencyObject相同的线程上创建DependencySource。
您知道为什么会发生这种情况吗?关于BackgroundWorker,这不是一个选项,无法将其设置为STA,我使用的某些图像转换需要它。出现错误似乎是因为我在另一个线程上创建位图。在位图上调用Freeze()
(例如myBitmap.Freeze()
)解决了错误,现在它似乎可以工作了。这听起来像是在另一个线程上创建了一些东西,应该是ui线程。您是否在线程上创建任何ui元素?哪个线程是集合的所有者?是的,位图必须被冻结才能在创建它们的线程之外的其他线程中使用。我一定会尝试一下这个任务。另一方面,BackgroundWorker似乎不是解决这个问题的方法,因为我无法将BackgroundWorker设置为STA,这是我使用的某些图像转换所必需的。然而,图像转换似乎确实是个问题。我在另一个线程上创建了位图。在这些位图上调用Freeze()。我将添加一些代码供参考,以供任务使用。。。祝你好运
Task.Factory.StartNew(() =>
{
var theTvdb = new TheTVDB();
var dexterSeries = theTvdb.SearchSeries("Dexter");
Application.Current.Dispatcher.Invoke(new Action(() =>
{
foreach (var tvSeries in dexterSeries)
{
this.Overview.Add(tvSeries);
}
}));
});