2012-03-05 73 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