求助:C#winform DataGridView性能问题

qq_39570852 2019-02-18 02:54:43
写了一个多线程的程序,界面添加了多个DataGridView但是在不同的TabPage里面,动态的按行添加数据但是只保留39行,超过39行后移除第一行在末尾再添加一行。由于添加数据频率比较快,会发生父窗体刷新窗体的时候出现部分界面刷新慢卡住,但DataGridView仍然在添加数据,线程操作DataGridView是通过Control.Invoke添加数据的。请大牛们帮忙分析出一个办法。如果有可以替代的不收费的控件也是可以的。
...全文
1679 38 打赏 收藏 转发到动态 举报
写回复
用AI写文章
38 条回复
切换为时间正序
请发表友善的回复…
发表回复
圣殿骑士18 2019-02-22
  • 打赏
  • 举报
回复
看起来,虽然一些细节看不明白,但总体上,你这代码就很糟糕。

1,while (true)
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


圣殿骑士18 2019-02-22
  • 打赏
  • 举报
回复
再说一句。

你程序的问题,性能的问题,应该主要在于两点
1. 使用了while(true)死循环

2. 你对invoke的处理不彻底,貌似在gridview填充时使用了BeginInvoke,但其他的UI元素还用invoke,而且代码还是在线程内执行?这相当于白搞。总结一句话就是你的异步调用不彻底,所以没用。

其他的,影响小,可能不一定是当前性能的瓶颈
圣殿骑士18 2019-02-22
  • 打赏
  • 举报
回复
引用 36 楼 qq_39570852 的回复:
谢谢,因为是菜鸟么所以会的不多。
1.用到死循环的是在单独的线程里面,信号量的WaitOne是为了没有数据的时候阻塞线程,互斥锁的WaitOne是因为线程同步。
2.SuspendLayout,ResumeLayout有尝试过去掉,没有什么区别。
3.因为HandleUpdateDataLog是单独线程所以就使用了Invoke
4.最后一张表使用BeginInvoke是因为实在UI线程里绑定的数据源。
5._DicSite2GridView[0].DataSource = _ArgDataTable;这个确实是第一次用,不是很了解BindingList还请您讲解一下

百度一下。
socg 2019-02-22
  • 打赏
  • 举报
回复
只要是涉及到绑定,线程更新、UI刷新,肯定会慢,我一般这种问题都是虚拟表实现,你的应用中,只显示最后N行,数据源可以一直累加,你只在绘制事件做对应就可以了,实际显示的哪一行完全由你来决定,例如table有100行,绘制事件要求你绘制第0行,你可以去100-N的行进行绘制
qq_39570852 2019-02-22
  • 打赏
  • 举报
回复
谢谢,因为是菜鸟么所以会的不多。
1.用到死循环的是在单独的线程里面,信号量的WaitOne是为了没有数据的时候阻塞线程,互斥锁的WaitOne是因为线程同步。
2.SuspendLayout,ResumeLayout有尝试过去掉,没有什么区别。
3.因为HandleUpdateDataLog是单独线程所以就使用了Invoke
4.最后一张表使用BeginInvoke是因为实在UI线程里绑定的数据源。
5._DicSite2GridView[0].DataSource = _ArgDataTable;这个确实是第一次用,不是很了解BindingList还请您讲解一下
jwh2004 2019-02-21
  • 打赏
  • 举报
回复
不要有一个新数据就实时添加一行,若1秒新增加20条信息,界面刷新 20次,就可能会闪烁感,1秒刷新2次~5次,人眼看的时候就舒服多了。
jwh2004 2019-02-21
  • 打赏
  • 举报
回复
线程只向全局变量List或dictionary中添加数据,添加时注意用lock锁下。界面0.5秒或1秒定时判断下全局变量是否有新数据,有就更新下界面显示,更新时,先调用DataGridView.BeginUpdate(); 添加完数据行后,再DataGridView.EndUpdate();
luj_1768 2019-02-21
  • 打赏
  • 举报
回复
可能问题在于:锁冲突或者同步策略。相关的解决方案需要参考后台数据库对你的数据的访问控制有关的设置参数。如果你想仿真大型数据库的并行操作,需要看好后台数据库的接入数设置,应该是一个线程对应一个固定接入。
weixin_43197510 2019-02-20
  • 打赏
  • 举报
回复
异步处理会好点吧 个人觉得
qq_39570852 2019-02-20
  • 打赏
  • 举报
回复
private void BindOnceDataSource(DataTable _ArgDataTable)
{
_DicSite2GridView[0].BeginInvoke(new Action(() =>
{
_DicSite2GridView[0].DataSource = _ArgDataTable;
}));
}
绑定数据源
qq_39570852 2019-02-20
  • 打赏
  • 举报
回复
private void HandleUpdateDataLog()
{
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
qq_39570852 2019-02-20
  • 打赏
  • 举报
回复
您说的异步处理,是不是也是画图的任务量是一样的呢,有更好的解决办法吗?
圣殿骑士18 2019-02-19
  • 打赏
  • 举报
回复
Control.Invoke

改成

Control.BeginInvoke

==========
欢迎关注微信公众号 “产品技术知与行” ,打造全面的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。
最新文章:多种Timer的场景应用 https://mp.weixin.qq.com/s/TJKi7PBj3nznf9FClirXUA
half_bucket 2019-02-19
  • 打赏
  • 举报
回复
出现界面卡顿及绘制不及时的问题,是否考虑使用遮罩呢?当数据加载完成,界面绘制完后,移除遮罩,绘制卡顿也许会同时消失
Chasmれ 2019-02-19
  • 打赏
  • 举报
回复
引用 14 楼 qq_39570852 的回复:
有什么办法吗?或者可以替代的不收费的控件

办法都给你说了,第一次赋值绑定数据源,后面的给行列赋值而不是重新绑定,就像下面这行代码
rDgv_AllInfos.Rows[index].Cells["aIsConnect"].Value = tran.IsConnect;

qq_39570852 2019-02-19
  • 打赏
  • 举报
回复
有什么办法吗?或者可以替代的不收费的控件
Chasmれ 2019-02-19
  • 打赏
  • 举报
回复
引用 12 楼 qq_39570852 的回复:
没开启双缓冲前确实闪烁,开启后解决了,但现在不是闪烁的问题,而是高频率添加和更新数据导致界面其他控件不能及时重绘
通过重新绑定数据源赋值,间隔时间太短,肯定会卡顿
qq_39570852 2019-02-19
  • 打赏
  • 举报
回复
没开启双缓冲前确实闪烁,开启后解决了,但现在不是闪烁的问题,而是高频率添加和更新数据导致界面其他控件不能及时重绘
Chasmれ 2019-02-19
  • 打赏
  • 举报
回复
间隔时间太短更换数据源会造成闪烁的,这个我之前试了不少方法,最后发现还是直接给行列赋值不闪烁
Chasmれ 2019-02-19
  • 打赏
  • 举报
回复
第一次导入数据绑定数据源,后面一行一行加,比如这样
 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;
                }
加载更多回复(18)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧