C#多线程向DataGridView添加数据仍UI冻结该如何解决?
优化方案
冻结根因
当前代码UI冻结的核心原因是高频调用Invoke抢占UI线程资源:每添加一列、每添加一行数据都会调度到UI线程执行,当数据量较大时,UI线程会被持续占用处理更新请求,无法响应用户其他操作,呈现冻结状态。同时手动逐行逐列添加控件的操作本身执行效率极低,进一步放大了卡顿问题。
优化措施
- 取消逐行逐列手动添加行列的操作,直接将处理完成的
DataTable通过DataSource属性批量绑定到DataGridView,可减少99%的UI操作量 - 降低进度更新频率,无需每一行/每一列都更新进度,改为按百分比(如每完成10%更新一次)或者每处理固定数量数据更新一次,大幅减少
Invoke调用次数 - 绑定数据前调用
SuspendLayout暂停DataGridView的布局逻辑,绑定完成后再调用ResumeLayout恢复布局,减少重复渲染开销 - 可开启
DataGridView的双缓冲特性,进一步优化渲染流畅度 - 优先使用
async/await异步方案替代手动创建Thread,无需手动处理Invoke逻辑,代码更易维护
优化后代码示例
AggregateFunction fun = (AggregateFunction)Enum.Parse(typeof(AggregateFunction), cmbAggregateFunc.Text); // 优先使用Task.Run代替手动创建Thread _ = Task.Run(() => prepareGrid(RowField, DataField, fun, ColumnFields)); private void prepareGrid(string RowField, string DataField, AggregateFunction aggregateFun, List<string> ColumnFields) { Invoke(new MethodInvoker(delegate { var mainForm = this.MdiParent as frmMain; mainForm.progressBarTop.Style = ProgressBarStyle.Blocks; mainForm.progressBarTop.Maximum = 100; mainForm.progressBarTop.Value = 20; mainForm.progressBarTop.Visible = true; mainForm.toolStripLabelTop.Text = "正在获取数据……"; dgvGrid.Visible = false; })); // 数据透视计算完全在后台线程执行,不涉及UI操作 Pivot pvt = new Pivot(dataTable); dtPivot = pvt.PivotData( RowField, DataField, aggregateFun, ColumnFields.ToArray<string>()); Invoke(new MethodInvoker(delegate { var mainForm = this.MdiParent as frmMain; mainForm.progressBarTop.Value = 80; mainForm.toolStripLabelTop.Text = "数据透视完成……"; })); if (dtPivot == null) { Invoke(new MethodInvoker(delegate { var mainForm = this.MdiParent as frmMain; mainForm.btn_Close.Enabled = true; mainForm.progressBarTop.Visible = false; mainForm.toolStripLabelTop.Text = ""; mainForm.btn_showReport.Enabled = true; MessageBox.Show("抱歉,未找到匹配数据。", Util.ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error); this.Close(); })); return; } else { Invoke(new MethodInvoker(delegate { var mainForm = this.MdiParent as frmMain; mainForm.btn_Export.Enabled = true; mainForm.btn_Close.Enabled = false; mainForm.toolStripLabelTop.Text = "正在绑定数据……"; mainForm.progressBarTop.Value = 90; // 批量绑定数据,替代原有循环逐行列添加逻辑 dgvGrid.SuspendLayout(); dgvGrid.DataSource = dtPivot; // 可批量设置列属性 foreach (DataGridViewColumn col in dgvGrid.Columns) { col.ReadOnly = true; } dgvGrid.ResumeLayout(); dgvGrid.Visible = true; // 重置UI状态 mainForm.btn_Close.Enabled = true; mainForm.progressBarTop.Visible = false; mainForm.toolStripLabelTop.Text = ""; })); } }
大数据量适配方案
如果处理的数据量超过10万行,可开启DataGridView的虚拟模式,仅渲染当前可视区域的行数据,不会一次性加载所有数据到控件中,可大幅提升操作流畅度。
内容的提问来源于stack exchange,提问作者Amit Khandelwal
相关产品推荐
相关产品推荐

