写了一个多线程的程序,界面添加了多个DataGridView但是在不同的TabPage里面,动态的按行添加数据但是只保留39行,超过39行后移除第一行在末尾再添加一行。由于添加数据频率比较快,会发生父窗体刷新窗体的时候出现部分界面刷新慢卡住,但DataGridView仍然在添加数据,线程操作DataGridView是通过Control.Invoke添加数据的。请大牛们帮忙分析出一个办法。如果有可以替代的不收费的控件也是可以的。
解决方案 »
- winform打包皮肤
- c# gridview 添加commandfield列,该列怎么分两行显示编辑和删除
- 如何将textbox1-4里的文字按行的输入到txt里呢
- 类是押注的程序,该怎么写???
- 控件问题
- SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间。
- 在水晶报表中怎样控制一页显示多少条记录
- disposed子窗体的问题
- 那位高手可以告诉我如何象C语言一样将一个结构直接存到文件中
- 注册页面的算法问题
- C#使用ReadProcessMemory读取内存,Win10能读取,Win7读出来全是0,是什么问题?
- 微信小程序 C#后台开发
//设置窗体的双缓冲
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles(); InitializeComponent(); //利用反射设置DataGridView的双缓冲
Type dgvType = this.dataGridView1.GetType();
PropertyInfo pi = dgvType.GetProperty("DoubleBuffered",
BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(this.dataGridView1, true, null);
要不自定义个界面,给数据分个页,可以参考下
https://www.cnblogs.com/qiaoke/p/8507183.html
里面的UiPageCodeMethod.cs
int index = GetRowsIndexOfIPAndPort(tran.IP,rDgv_AllInfos,tran.Port);
if (tran != null && (index > 0))
{
rDgv_AllInfos.Rows[index].Cells["aIsConnect"].Value = tran.IsConnect;
rDgv_AllInfos.Rows[index].Cells["aClientIsInstal"].Value = tran.IsInstal;
rDgv_AllInfos.Rows[index].Cells["aMrUnningStatus"].Value = tran.RunState;
rDgv_AllInfos.Rows[index].Cells["aMBRAMofRmu"].Value = tran.RmuRam;
rDgv_AllInfos.Rows[index].Cells["aServRunType"].Value = tran.Runtype;
}
rDgv_AllInfos.Rows[index].Cells["aIsConnect"].Value = tran.IsConnect;
欢迎关注微信公众号 “产品技术知与行” ,打造全面的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。
最新文章:多种Timer的场景应用 https://mp.weixin.qq.com/s/TJKi7PBj3nznf9FClirXUA
@SoulRed RX.net能具体说说吗?
或许是加载数据方法不对,一个Cell 一个 Cell 的 加数据 确实或许会慢一些。我也替你找找 有没有更快的 加数据的方式。
private void BindByDataSource(DataTable table)
{
//3800行 x 26列, 总计 10W 个单元格
//直接用 DataSource 绑定, 耗时 0.09秒 DateTime time0 = DateTime.Now;
dataGridView1.DataSource = table;
DateTime time1 = DateTime.Now;
Console.WriteLine(string.Format("通过 DataSource 绑定 {0}x{1} 数据, 耗时 {2}秒", table.Rows.Count, table.Columns.Count, (time1 - time0).TotalSeconds));
}
private void BindByCell(DataTable table)
{
DateTime time0 = DateTime.Now; foreach (DataColumn col in table.Columns)
{
dataGridView1.Columns.Add(col.ColumnName, col.ColumnName);
} //3800行 x 26列, 总计 10W 个单元格
foreach (DataRow row in table.Rows)
{
//只执行这行代码 耗时 0.6秒
int i = dataGridView1.Rows.Add();
var row2 = dataGridView1.Rows[i]; //通过 名称赋值, 耗时 0.73-0.6 = 0.13秒
foreach (DataColumn col in table.Columns)
row2.Cells[col.ColumnName].Value = row[col]; ////通过 索引赋值, 耗时 0.71-0.6 = 0.11秒
//for (int j = 0, c = table.Columns.Count; j < c; j++)
// row2.Cells[j].Value = row[j];
}
DateTime time1 = DateTime.Now;
Console.WriteLine(string.Format("通过 Cell 赋值 {0}x{1} 数据, 耗时 {2}秒", table.Rows.Count, table.Columns.Count, (time1 - time0).TotalSeconds));
}
DataGridView 很大一部分性能损失,是在 dataGridView1.Rows.Add(); 这行代码。我的示例 3800行 x 26列, 总计 10W 个单元格
—— 楼主的示例是 39行?? 我是真看不懂 性能会浪费在什么地方了。
而且双缓冲建议不要开启,记得看过一个帖子分析过,这是无奈之举,使用常规的异步比它好。
{
GlobalStaticClass.DataLogViewThreadStopped = false;
while (true)
{
_DatalogThreadSem.WaitOne();
if (GlobalStaticClass.HandleStatisticThreadStopped && GlobalStaticClass.bCloseAllRequest)
break;
foreach (KeyValuePair<int, DataLogTableClass> _TempKeyValuePair in _DicSite2DataLogTableClass)
{
if (_DicSite2GridView.TryGetValue(_TempKeyValuePair.Key, out _DataGridView))
{
if (_TempKeyValuePair.Key == 0 )
{
continue;
}
else if (_TempKeyValuePair.Key == -1)//QA
{
this.Invoke(new Action(() => {
foreach (KeyValuePair<int, DataLogTableClass> _QATempDataLogPair in _DicQASite2DataLogTableClass)
{
if (_QATempDataLogPair.Value._ForShowDataTable.Rows.Count > 1)
{
if (_DataGridView.Rows.Count >= (_FirstDataRowIndex + _KeepDataRowCount))
_DataGridView.Rows.RemoveAt(_FirstDataRowIndex);
_DataGridView.Rows.Add(_QATempDataLogPair.Value._ForShowDataTable.Rows[0].ItemArray);
_QATempDataLogPair.Value._ForShowDataTable.Rows.RemoveAt(0);
}
}
if (_DataGridView.Rows.Count > _FirstDataRowIndex)
_DataGridView.FirstDisplayedScrollingRowIndex = _DataGridView.Rows.Count - 1;
}));
}
else //Site 1,2,3..
{
this.Invoke(new Action(() =>
{
_DataGridView.SuspendLayout();
if (_DataGridView.Rows.Count >= (_FirstDataRowIndex + _KeepDataRowCount))
_DataGridView.Rows.RemoveAt(_FirstDataRowIndex);
_DatalogThreadMutex.WaitOne();
if (_TempKeyValuePair.Value._ForShowDataTable.Rows.Count > 0)
{
_DataGridView.Rows.Add(_TempKeyValuePair.Value._ForShowDataTable.Rows[0].ItemArray);
//_DataGridView.FirstDisplayedScrollingRowIndex = _DataGridView.Rows.Count - 1;
// Monitor.Enter(_TempKeyValuePair.Value._ForShowDataTable);
_TempKeyValuePair.Value._ForShowDataTable.Rows.RemoveAt(0);
//Monitor.Exit(_TempKeyValuePair.Value._ForShowDataTable);
}
_DatalogThreadMutex.ReleaseMutex();
if (_DataGridView.Rows.Count > _FirstDataRowIndex)
_DataGridView.FirstDisplayedScrollingRowIndex = _DataGridView.Rows.Count - 1;
_DataGridView.ResumeLayout();
}));
}
}
}
}
GlobalStaticClass.DataLogViewThreadStopped = true;
}
按行添加更新DataGridView
{
_DicSite2GridView[0].BeginInvoke(new Action(() =>
{
_DicSite2GridView[0].DataSource = _ArgDataTable;
}));
}
绑定数据源
p哥多次反复提到,不要采用死循环的方式来实现需求,应该多考虑用事件。基本可以肯定,你用死循环,效率肯定不高。2, 看到多个地方用到了 WaitOne();
这种等待操作本来就是要等待的,这不就是卡界面的因素吗?但我没看明白WaitOne()的用途。但如果你解决了第一点,用事件代替死循环,就算有WaitOne,那也不会影响性能,因为它在UI刷新事件的驱动端,和事件的接收端已经分离。3、你还用了SuspendLayout,ResumeLayout
真是什么重量级的东西都用上了,这些本来都是Designer.cs才会有的东西。我是从来不用这些,因为没必要。4、如果你的HandleUpdateDataLog方法是在独立线程里执行的,那么它里边怎么还有好几个this.Invoke()?还有SuspendLayout,ResumeLayout这些操纵UI的方法,那你在最后刷新grid的时候改BeginInvoke有什么用?所有涉及到界面的修改都应该在BeginInvoke里的!5、最后绑定数据源的方式,也是低效的。一般程序员只会这种,但要用的更好,这是不够的
_DicSite2GridView[0].DataSource = _ArgDataTable;
我都是用BindingList来实现部分更新,而不是整体替换数据源。总结起来感觉是:这个代码不忍看。效率低是正常的。==========
欢迎关注微信公众号 “产品技术知与行” ,打造全面的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。
最新文章:解读经典《C#高级编程》 第四章之继承.接口 https://mp.weixin.qq.com/s/fO4C0WAYZUJYEvKJiBCuVA
1.用到死循环的是在单独的线程里面,信号量的WaitOne是为了没有数据的时候阻塞线程,互斥锁的WaitOne是因为线程同步。
2.SuspendLayout,ResumeLayout有尝试过去掉,没有什么区别。
3.因为HandleUpdateDataLog是单独线程所以就使用了Invoke
4.最后一张表使用BeginInvoke是因为实在UI线程里绑定的数据源。
5._DicSite2GridView[0].DataSource = _ArgDataTable;这个确实是第一次用,不是很了解BindingList还请您讲解一下
1. 使用了while(true)死循环2. 你对invoke的处理不彻底,貌似在gridview填充时使用了BeginInvoke,但其他的UI元素还用invoke,而且代码还是在线程内执行?这相当于白搞。总结一句话就是你的异步调用不彻底,所以没用。其他的,影响小,可能不一定是当前性能的瓶颈