2010-07-07 37 views
3

我是一個使用.NET框架的初級開發人員。當大數據量的數據時,.NET GUI凍結

我正在處理一個問題,因爲當我用大量數據運行我的應用程序時,我的GUI凍結。

我有一個網格輸出文本框來記錄字符串。網格對於預期到達的每條消息都有一行。

然後,應用程序接收消息,網格更新與該消息相對應的行中的單元格。另外,我在文本框中寫入了一個關於該消息的信息的字符串。

例如,文本框將有消息,如:

10:23:45 Message 1 arrived and the result is OK 
10:23:45 Message 2 arrived and the result is OK 
10:23:45 Message 3 arrived and the result is FAIL 
10:23:45 Message 4 arrived and the result is OK 
10:23:46 Message 5 arrived and the result is OK 
.... 

和電網會是這樣的:

MESSAGE_ID | RESULT <------- HEADER 
Message_1 | OK 
Message_2 | FAIL 
Message_3 | OK 
Message_4 | OK 
Message_5 | OK 
Message_6 | Waiting 
Message_7 | Waiting 
.... 

的問題是,當我在很短的接收多條消息時間,GUI凍結,因爲它始終更新網格和文本框。它會凍結,直到所有消息到達並且更新了網格和文本輸出。

您是否知道是否有某種方式可以通過某種方式來實現GUI不凍結?使用多個線程來更新GUI?

我認爲這不是一個BackgroundWorker,因爲GUI是應該做的工作,但也許我錯了。

EDITED1:

其實我有兩個線程:

1)主線程。這是GUI,它有一個BlockingQueue。

private BlockingQueue _guiQueue = new BlockingQueue(1000); 

2)線程1 它接收消息,在接收到消息後,做了一些工作,然後它會將結果,並將其發送到GUI:

_guiQueue.Enqueue(new UpdateResult(_message.Name, _message.Result)); 

我使用BlockingQueues ,這個: http://www.codeproject.com/KB/recipes/boundedblockingqueue.aspx

一旦主線程收到消息,它基本上更新網格和輸出文本框,沒有別的。

public MainThread(IMainForm mainView) 
    { 
     // presenter 
     _mainView = mainView; 
     .... 
    // Blocking queues 
     _guiQueue = new BlockingQueue(1000); 
     .... 
     // Timer 
     logger.Debug("Initializing Timer"); 
     _timer = new DispatcherTimer(); 
     _timer.Interval = TimeSpan.FromMilliseconds(10); 
     // Call handleMessages method everytime the timer wakes up 
     _timer.Tick += HandleMessages; 
     _timer.Start(); 
     ... 
     // Order Passing Thread 
     logger.Debug("Launching OPThread"); 
     _orderPassingThread = new OPThread(_OPQueue, _commonObjects); 
     _orderPassingThreadProcess = new Thread(new ThreadStart(_orderPassingThread.OPThreadProcess)); 
     _orderPassingThreadProcess.Start(); 
     ... 
    } 

    private void HandleMessages(Object sender, EventArgs args) 
    { 
     Presenter.Messages.Message message; 

     while ((message = _guiQueue.Dequeue(10)) != null) 
     { 
      switch (message.MessageType) 
      { 
       case messageTypes.updateResult: 
        UpdateResult updateStepMsg = (UpdateResult) message;   
        _mainView.updateStepResult(updateStepMsg.Name, updateStepMsg.Result); // updates    Grid and text box   
     break; 
      .... 
       default: 
        break; 
      } 
     } 
    } 

}

問題是,當我接收多於一個消息的第二左右。

通過實例,我有一個STOP按鈕停止一切,但沒有辦法來點擊它,因爲GUI是凍結

謝謝!

PS:我使用的DevExpress,網格是XtraGrid中,輸出文本框是memoEdit控制

+0

你能告訴我們代碼產生bac kground線程?包含while循環的方法的全部內容? – 2010-07-07 09:42:39

+0

添加了該代碼。隊列是這些: http:// www。codeproject.com/KB/recipes/boundedblockingqueue.aspx?bcsi_scan_1B08F4CB0D5234CC=BK/P3lTlELaTMlVRd7WPTA4AAADxi/AF&bcsi_scan_filename=boundedblockingqueue.aspx – pedroruiz 2010-07-07 09:57:07

+0

如果這是Java的,我可能會在GUI線程策略上進行同步:_mainView.updateStepResult(updateStepMsg.Name,updateStepMsg.Result ) 也許,你可以得到線索: http://stackoverflow.com/questions/530211/creating-a-blocking-queuet-in-net – eee 2010-07-07 10:16:32

回答

0

CPU密集型如何爲您的信息receival代碼?

嘗試使用BackgroundWorker線程處理消息接收,然後才更新GUI。

This應該可以幫助你。

乾杯

+0

謝謝。我剛剛更新了這篇文章,並添加了一些相關信息。 – pedroruiz 2010-07-07 09:41:44

2

一種常見的反模式與消息處理和圖形用戶界面是因爲它的接收立即響應每個消息。一種更好的方法通常是在收到消息時排隊消息,然後僅定期更新GUI,每250ms一個定時器。當你想要更新UI時,請使用更新它的有效方法。許多專業UI組件具有「BeginUpdate \ EndUpdate」的概念,其中可以應用一批更改,而不必在每次更改應用UI時更新UI。

更新

你不應該使用ConcurrentQueue?一個BlockingQueue會阻止讀者(即在你的情況下UI的外觀),直到有可用的數據。

+0

也許這就是原因,我使用這些隊列 的:HTTP://www.codeproject.com/KB/recipes/boundedblockingqueue.aspx bcsi_scan_1B08F4CB0D5234CC = BK/P3lTlELaTMlVRd7WPTA4AAADxi/AF&bcsi_scan_filename = boundedblockingqueue.aspx – pedroruiz 2010-07-07 09:57:33

+0

@pedroruiz該鏈接不爲我工作,所以我無法檢查阻止隊列的行爲。嘗試使用ConcurrentQueue,如果您使用的是.NET 4.0。 – 2010-07-07 10:06:20

+0

不幸的是我在網上3.5。 但是,如果該BlockingQueue實現阻止讀者,直到有可用的數據,不會我的GUI始終凍結?例如,如果只有消息每3秒到達一次,它不會凍結。只有當時間超過1秒鐘左右。 謝謝 – pedroruiz 2010-07-07 10:15:29

1
  1. 使用線程做背景計算的東西。 (BackGroundWorker)
  2. 而不是更新每個新事件的屏幕,存儲數據。運行一個定時器,以便每隔X秒一次,它將當前數據寫入屏幕。屏幕需要改變,以便人們看到有東西進入,而不是每秒顯示最新的信息數百次。
0

我認爲一個好的方法是使用XtraGrid的教程之一中顯示的解決方案。請看看Demos \ Components \ XtraGrid \ CS \ GridTutorials \ GridVirtualData文件夾。它包含演示項目,演示如何有效處理大數據。在這個示例中,網格非常快速地處理包含100000條記錄的集合。網格行由索引表示,實數值使用屬性描述符獲得。因此,當網格被加載時,它只提取屏幕上可見的行。當網格滾動時,動態訪問其他數據。設置斷點在

對象IList.this的吸氣劑[INT fIndex]

財產,你會看到網格可以有多聰明是:)。

要解凍GUI,可以使用Application.DoEvents()方法。

我看到您正在使用MemoEdit來記錄輸入消息。它包含標準的多行TextBox,這個控件的工作速度非常慢,大內容:(如果我理解你的任務正確,你已經添加了編輯器來允許最終用戶複製輸入消息。如果我是你,我會替換。在XtraGrid中的MemoEdit它允許你將數據複製從幾個選定的記錄到剪貼板

我們已經略有改變示範項目,這裏是生成的代碼,我們終於得到了:

List<LogMessage> list = new List<LogMessage>(); 
for(int i = 0;i < 100000;i++) 
    list.Add(new LogMessage()); 
vList = new VirtualList(list); 
grid.DataSource = vList; 

.. 。

public class LogMessage { 
     public LogMessage() { 
      TimeStamp = DateTime.Now; 
      Description = "Message at " + TimeStamp.Ticks.ToString(); 
     } 
     public DateTime TimeStamp; 
     public string Description; 
    } 

    public abstract class LogMessagePropertyDescriptor : PropertyDescriptor { 
     bool fIsReadOnly; 

     public LogMessagePropertyDescriptor(string fPropertyName, bool fIsReadOnly) 
      : base(fPropertyName, null) { 
      this.fIsReadOnly = fIsReadOnly; 
     } 

     public override bool CanResetValue(object component) { return false; } 
     public override bool IsReadOnly {get { return fIsReadOnly; } } 
     public override Type ComponentType { get { return typeof(LogMessage); } } 
     public override void ResetValue(object component) {} 
     public override bool ShouldSerializeValue(object component) { return true; } 
    } 
    public class LogMessageTimeStampPropertyDescriptor: LogMessagePropertyDescriptor { 
     public LogMessageTimeStampPropertyDescriptor(bool fIsReadOnly) 
      : base("TimeStamp", fIsReadOnly) { 
     } 
     public override Type PropertyType {get {return typeof(DateTime); } } 
     public override object GetValue(object component) { 
      LogMessage message = (LogMessage)component; 
      return message.TimeStamp; 
     } 
     public override void SetValue(object component, object val) { 
      LogMessage message = (LogMessage)component; 
      message.TimeStamp = (DateTime)val; 
     } 
    } 
    public class LogMessageDescriptionPropertyDescriptor: LogMessagePropertyDescriptor { 
     public LogMessageDescriptionPropertyDescriptor(bool fIsReadOnly) 
      : base("Description", fIsReadOnly) { 
     } 

     public override Type PropertyType { get { return typeof(string); } } 

     public override object GetValue(object component) { 
      LogMessage message = (LogMessage)component; 
      return message.Description; 
     } 
     public override void SetValue(object component, object val) { 
      LogMessage message = (LogMessage)component; 
      message.Description = (string)val; 
     } 
    } 
    public class VirtualList : IList, ITypedList { 
     PropertyDescriptorCollection fColumnCollection; 
     List<LogMessage> messages; 

     public VirtualList(List<LogMessage> messages) { 
      this.messages = messages; 
      CreateColumnCollection(); 
     } 
     public int RecordCount { get {return messages.Count; } } 
     public int ColumnCount { get { return fColumnCollection.Count; } } 
     protected virtual void CreateColumnCollection() { 
      List<PropertyDescriptor> pds = new List<PropertyDescriptor>(); 
      pds.Add(new LogMessageTimeStampPropertyDescriptor(true)); 
      pds.Add(new LogMessageDescriptionPropertyDescriptor(false)); 
      fColumnCollection = new PropertyDescriptorCollection(pds.ToArray()); 
     } 

     #region ITypedList Interface 
     object IList.this[int fIndex] { get { return messages[fIndex]; } set { } } 
     PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] descs) { return fColumnCollection; } 
     string ITypedList.GetListName(PropertyDescriptor[] descs) { return ""; } 
     #endregion 
     #region IList Interface 
     public virtual int Count { get { return RecordCount; } } 
     public virtual bool IsSynchronized { get { return true; } } 
     public virtual object SyncRoot { get { return true; } } 
     public virtual bool IsReadOnly{ get { return false; } } 
     public virtual bool IsFixedSize{ get { return true; } } 
     public virtual IEnumerator GetEnumerator() { return null; } 
     public virtual void CopyTo(System.Array array, int fIndex) {} 
     public virtual int Add(object val) { throw new NotImplementedException(); } 
     public virtual void Clear() { throw new NotImplementedException(); } 
     public virtual bool Contains(object val) { throw new NotImplementedException(); } 
     public virtual int IndexOf(object val) { throw new NotImplementedException(); } 
     public virtual void Insert(int fIndex, object val) { throw new NotImplementedException(); } 
     public virtual void Remove(object val) { throw new NotImplementedException(); } 
     public virtual void RemoveAt(int fIndex) { throw new NotImplementedException(); } 
     #endregion 
    }