运行后台线程数百万次
本文关键字:数百万 线程 后台 运行 | 更新日期: 2023-09-27 18:26:35
我正在开发一个WPF GUI(.net 3.5),该GUI具有绑定到数据网格的BindingList
。我处理BindingList
的ListChangedEvent
,因为我对同一BindingList
执行昂贵的linq查询,并更新linq结果中的某些属性(更新后将在GUI上正确引发和调度INotifyPropertyChanged.PropertyChanged
事件)。
我的绑定列表可能已经有数百万条记录,GUI有时可能在几秒钟内对这些记录执行数百万次事务(添加或删除)。因此,每个事务都会引发ListChangedEvent
事件。
这就是我的GUI受到影响的地方,所以我在后台线程上移动了linq检查。但交易是这样的。。。
- 为了获得一致的结果,我必须在收到的每个
ListChangedEvent
上生成一个新的后台线程。这将导致数百万线程在几秒钟内在内存中生成。。。内存上的一个非常大的瓶颈 - 我可能使用一个后台工作程序,但它在完成之前无法运行新的异步工作,所以我们将不得不在GUI线程上等待。GUI将挂起,这又是一个问题
-
我可以检查bgWorker.IsBusy(),但这将跳过工作人员繁忙时处理的许多
ListChangedEvent
,从而失去linq查询的完整性。 -
假设我找到了解决上述第2点和第3点中讨论的问题的方法,我无论如何都应该在我所做的每一项工作中都依赖于百万记录集合的快照。这将导致大量本地百万记录集合被创建和GCed,也许在几秒钟内。。。
所以我很困惑什么解决方案最适合我。。。
现有伪代码:
....
var bindingList = GetMillionsOfRecordsBindingList();
bindingList.ListChanged += OnEachItemChanged;
mydataGrid.ItemsSource = bindingList;
....
private void OnEachItemChanged(object sender, ListChangedEventArgs e)
{
var currentStauses = GetCurrentStatuses();
var matchedItems = from p in bindingList
where p.RefID != null
and curStauses.Any(status => status.Type == p.Status.Type)
select p;
// We have checked that in production the matchedItems collection
// less than hundred items updated with their statuses at a time.
foreach(var p in matchedItems)
{
p.ShowHighlightAnimation = true; //GUI runs animations on the updated items.
}
}
我建议的伪代码:
private void OnEachItemChanged(object sender, ListChangedEventArgs e)
{
var bgWrkr = new BackgroundWorker();
// This will spawn millions of threads
// (even if they wait in threadpool we have million threads to finish)
bgWrker.DoWork
+= (o, args) =>
{
var listOfItems = args.Argument as List<MyItems>;
var currentStauses = GetCurrentStatuses();
var matchedItems = from p in bindingList
where p.RefID != null
&& curStauses.Any(status => status.Type == p.Status.Type)
select p;
foreach(var p in matchedItems)
{
p.ShowHighlightAnimation = true;
//GUI runs animations on the updated items.
}
};
bgWrkr.RunWorkerAsync(bindingList.ToList());
}
我知道这个代码同样糟糕。。。。因此,我对正确的方法感到困惑!
注意:我无法控制执行绑定列表上这一百万个事务的其他进程。它可能在几秒钟内完成,也可能在一天内悠闲地完成。因此,LINQ同步(我正在另一个线程上进行)仅在该进程结束时是不可能的
BackgroundWorker
提供了一个很好的简单界面,可以在有离散工作要执行时实现后台处理。它还有一个很好的特性,它提供了一个通过UI线程更新控件的简单机制。然而,它有点有限。
使用ThreadPool怎么样?这允许您使用大量线程来执行任务。您可以通过QueueUserWorkItem将任务发送到线程池。但是,您必须通过Dispatcher.BeginInvoke
将更新封送回UI线程。