我是使用.NET框架的初级开发人员。
我正在处理一个问题,因为当我运行带有大量数据的应用程序时,我的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)Thread1 它接收消息,在收到消息后执行某些操作,然后将结果排队并将其发送到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;
}
}
}
}
问题是我每隔一秒收到一条以上的邮件。
例如,我有一个停止按钮来停止一切,但由于GUI冻结,无法点击它
谢谢!
PS:我正在使用DevExpress,网格是XtraGrid,输出文本框是memoEdit控件
答案 0 :(得分:2)
带有消息处理和GUI的常见反模式是在收到消息时立即响应。更好的方法通常是在收到消息时对消息进行排队,然后仅定期更新GUI,例如每250毫秒一个计时器。当您确实更新UI时,请使用有效的更新方法。许多专业UI组件都具有“BeginUpdate \ EndUpdate”的概念,其中可以应用一批更改,而不会在应用时对每个更改进行UI更新。
<强>更新强>
你不应该使用ConcurrentQueue吗?在有可用数据之前,BlockingQueue会阻止读者(即在你的情况下是UI的外观)。
答案 1 :(得分:1)
答案 2 :(得分:0)
答案 3 :(得分:0)
我认为一个好的方法是使用XtraGrid教程中的一个解决方案。请查看Demos \ Components \ XtraGrid \ CS \ GridTutorials \ GridVirtualData文件夹。它包含演示如何有效处理大数据的演示项目。在此示例中,网格工作非常快,包含100000条记录的集合。网格行由索引表示,实际值使用属性描述符获得。因此,当加载网格时,它仅获取屏幕上可见的行。滚动网格时,动态访问其他数据。在
的getter中设置断点对象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
}