2012-03-05 67 views
1

我正在开发一个绑定到数据网格的BindingList的WPF GUI(.net 3.5)。我处理BindingListListChangedEvent,并且我在同一个BindingList上执行昂贵的linq查询并更新linq结果中的某些属性(,它在更新时正确地在GUI上提升和调度INotifyPropertyChanged.PropertyChanged事件)。运行后台线程(百万次)

我的绑定列表可能已经有数百万条记录,GUI有时可能会在几秒钟内在这些记录上执行数百万次事务(添加或删除)。所以每次交易都会引发ListChangedEvent事件。

这是我的GUI正在影响,所以我移动LINQ检查后台线程。但这里是这笔交易......

  1. 为了得到一致的结果,我必须在产卵一个新的后台线程每个ListChangedEvent接收。这将导致数百万个线程在几秒钟内在内存中产生......内存中的一个非常大的瓶颈。
  2. 我可能会使用单个后台工作器,但它不能运行新的异步工作直到完成,因此我们将不得不在GUI线程上等待。 GUI会挂起,这又是一个问题!
  3. 我可以检查bgWorker.IsBusy(),但这将跳过很多ListChangedEvent处理,同时工作人员繁忙,从而失去了linq查询的完整性。

  4. 想我找到出路,以解决点号2和3以上讨论过这个问题,我反正靠每个ASYC工作我做的百万记录收集的快照。这将导致大量的越来越创建的本地万人次的纪录集合和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同步(我在另一个线程上进行的)只在该进程结束时出现问题。

回答

1

BackgroundWorker提供了一个很好的简单界面,当你有一个独立的工作要执行时,实现后台处理。它还具有很好的功能,它提供了一个通过UI线程更新控件的简单机制。但是,这是有限的。

使用ThreadPool怎么样?这使您可以使用大量线程执行任务。您可以通过QueueUserWorkItem将任务发送到线程池。但是,您将不得不通过Dispatcher.BeginInvoke将更新封送回UI线程。

+0

@CilnE thx的评论...但不会线程队列线程?它可以排列数百万?会有什么影响?我想那时我在每个改变的事件上创建新的后台工作者,apporach也可以作为后台工作者本身在内部使用threapool! – 2012-03-05 06:33:07

+1

ThreadPool专为排队大量需要在有限数量的线程上运行的任务而设计。 – zabulus 2012-03-05 06:38:58

+0

@zabulus,Jon Skeet写了这篇文章http://www.yoda.arachsys.com/csharp/multithreading.html关于后台工作者如何在内部使用线程池以及由于排队和由于排队等原因可能需要很长时间完成任务...所以我的GUI更新在预计的持续时间内保证? – 2012-03-05 06:40:57