You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForms中BackgroundWorker填充DataGridView跨线程错误修复求助

错误原因及修复方案

核心问题分析

  1. 跨线程访问UI控件:DoWork事件在后台线程执行,直接调用的FillGrid中存在对dtpFromdt、dtpToDate(读取值)和dgvStock(绑定数据)的操作,这些控件由UI线程创建,后台线程直接操作会触发跨线程错误。
  2. BackgroundWorker被提前释放:RunWorkerCompleted中调用backgroundWorker1.Dispose(),导致第二次点击搜索时控件已被释放,无法复用。
  3. 取消逻辑无效:CancelAsync()仅设置取消标记,但后台线程未检查CancellationPending,无法真正终止任务。
  4. ProgressChanged滥用:ProgressChanged运行在主线程,其中的Thread.Sleep(20000)会直接阻塞UI,属于错误用法。

具体修复步骤

1. 拆分数据获取与UI操作

将耗时的数据库查询放在后台线程执行,UI绑定操作必须回到主线程完成。

2. 传递参数给后台线程

启动BackgroundWorker前在主线程读取日期控件的值,作为参数传递给后台线程,避免后台线程直接访问UI控件。

3. 保留BackgroundWorker实例

移除RunWorkerCompleted中的backgroundWorker1.Dispose(),确保控件可复用。

4. 完善取消逻辑

启用WorkerSupportsCancellation,后台线程中定期检查CancellationPending标记,及时终止任务。

5. 清理无效的ProgressChanged代码

ProgressChanged仅用于更新进度(如进度条),移除其中的阻塞操作。


完整修正代码

private clsStkBAL obj = new clsStkBAL();

public frmSalesStk()
{
    InitializeComponent();
    backgroundWorker1.WorkerReportsProgress = true;
    backgroundWorker1.WorkerSupportsCancellation = true; // 启用取消支持
}

// 仅在主线程执行的UI绑定方法
private void BindGrid(DataTable dt)
{
    try
    {
        clsCommon.BindeDataGridView(this.dgvStock, dt, false);
    }
    catch (Exception ex)
    {
        clsCommon.ShowErrorMessage(ex.Message);
    }
}

// 后台线程执行的数据获取方法
private DataTable GetData(DateTime startDate, DateTime endDate)
{
    try
    {
        this.obj.SP_STATUS = nameof(FillGrid);
        this.obj.Sih_StartDate = startDate;
        this.obj.Sih_EndDate = endDate;
        
        // 检查是否被取消,及时终止任务
        if (backgroundWorker1.CancellationPending)
            return null;
            
        return this.obj.GetDataTable((clsSalesStock)this.obj, null, false) as DataTable;
    }
    catch (Exception ex)
    {
        // 后台线程不能直接弹消息,将异常传递到RunWorkerCompleted
        throw ex;
    }
}

private void btnSearch_Click(object sender, EventArgs e)
{
    if (backgroundWorker1.IsBusy)
    {
        clear();
        return;
    }
    
    // 主线程读取日期参数,避免跨线程访问
    var startDate = dtpFromdt.Value.Date;
    var endDate = dtpToDate.Value.Date;
    
    // 传递参数给后台线程
    backgroundWorker1.RunWorkerAsync(new { StartDate = startDate, EndDate = endDate });
    
    // 禁用日期控件防止重复操作
    dtpFromdt.Enabled = false;
    dtpToDate.Enabled = false;
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    var args = e.Argument as dynamic;
    DateTime startDate = args.StartDate;
    DateTime endDate = args.EndDate;
    
    // 后台线程执行数据获取
    DataTable dt = GetData(startDate, endDate);
    
    // 如果被取消,设置取消结果
    if (backgroundWorker1.CancellationPending)
    {
        e.Cancel = true;
        return;
    }
    
    // 将查询结果传递给RunWorkerCompleted
    e.Result = dt;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // 恢复控件可用状态
    dtpFromdt.Enabled = true;
    dtpToDate.Enabled = true;
    
    if (e.Cancelled)
    {
        MessageBox.Show("任务已取消");
        return;
    }
    
    if (e.Error != null)
    {
        clsCommon.ShowErrorMessage(e.Error.Message);
        return;
    }
    
    DataTable dt = e.Result as DataTable;
    if (dt != null)
    {
        BindGrid(dt); // 主线程绑定数据
        MessageBox.Show("加载完成");
    }
}

private void frmSalesStock_Load(object sender, EventArgs e)
{
    dtpFromdt.Value = new DateTime(2021, 02, 01);
    dtpToDate.Value = new DateTime(2021, 02, 01);
    // 若设计器已绑定DoWork事件,无需重复绑定
    // backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
}

public void clear()
{
    if (backgroundWorker1.IsBusy)
    {
        backgroundWorker1.CancelAsync();
    }
    dgvStock.DataSource = null;
}

private void btnClear1_Click(object sender, EventArgs e)
{
    clear();
}

关键修复点说明

  • 参数传递:主线程提前读取日期值并传递给后台线程,彻底避免跨线程访问UI控件。
  • 操作分离:GetData在后台线程执行耗时查询,BindGrid在主线程完成UI绑定,实现线程安全隔离。
  • 取消逻辑:启用取消支持后,后台线程实时检查取消标记,及时终止任务。
  • 控件复用:移除Dispose调用,保证BackgroundWorker可多次使用。

内容的提问来源于stack exchange,提问作者gok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 20:57:02