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

如何在BackgroundWorker中更新DataTable记录并解决重复执行报错问题

解决BackgroundWorker并发报错并实现带进度条的DataTable批量更新

首先,你遇到的"This BackgroundWorker is currently busy..."报错原因很直接:当BackgroundWorker正在执行后台任务时,你再次调用了RunWorkerAsync()。它同一时间只能处理一个任务,所以得先堵住重复点击的问题,然后还要把你的代码逻辑调整得更合理——你原来的写法把耗时的数据库更新放在了主线程,BackgroundWorker只是在模拟进度,这完全颠倒了它的用途。

下面一步步给你解决:

核心修复点

1. 阻止重复点击触发任务

在按钮的点击事件里,先检查BackgroundWorker的IsBusy状态,如果它正在忙,直接提示用户或者返回,别让新任务启动:

if (backgroundWorker1.IsBusy)
{
    MessageBox.Show("更新任务正在进行中,请稍候!");
    return;
}

2. 把耗时的数据库操作移到后台线程

你原来的代码在主线程做批量SQL更新,这会导致UI卡顿,BackgroundWorker的核心作用就是把这类耗时操作扔到后台,主线程专注于更新UI(比如进度条)。所以要把数据库更新的逻辑全部移到DoWork事件里。

3. 正确配置BackgroundWorker属性

WorkerReportsProgress要在任务启动前设置好,别在DoWork的循环里反复设置——最好在窗体初始化时就配置:

public YourFormName()
{
    InitializeComponent();
    backgroundWorker1.WorkerReportsProgress = true;
    backgroundWorker1.WorkerSupportsCancellation = true; // 可选,允许用户取消任务
}

4. 线程安全地传递UI数据

后台线程不能直接访问UI控件(比如dataGridView1、company_name文本框),会引发跨线程异常。所以要把需要的参数打包成对象,通过RunWorkerAsync()传递给后台。

5. 准确计算进度百分比

你原来的进度计算是直接用循环次数当百分比,如果DataGridView的行数不是100,进度条永远到不了100%。正确的计算方式是:(已处理行数 / 总行数) * 100。

6. 任务完成后的UI更新要放在RunWorkerCompleted

别在主线程里直接弹MessageBox,因为后台任务还没完成就会触发提示,要等后台任务结束后,在RunWorkerCompleted事件里处理结果和提示。


完整修正后的代码

窗体初始化配置

public YourFormName()
{
    InitializeComponent();
    // 提前配置BackgroundWorker的属性
    backgroundWorker1.WorkerReportsProgress = true;
    backgroundWorker1.WorkerSupportsCancellation = true;
}

按钮点击事件

private void btnStartUpdate_Click(object sender, EventArgs e)
{
    // 防止重复触发任务
    if (backgroundWorker1.IsBusy)
    {
        MessageBox.Show("更新任务正在进行中,请稍候!");
        return;
    }

    // 打包需要传递给后台的参数(避免后台线程直接访问UI控件)
    var updateParameters = new
    {
        CompanyName = company_name.Text,
        IndebtednessName = indebtedness_name.Text,
        RowsToUpdate = dataGridView1.Rows.Cast<DataGridViewRow>()
                                        .Where(row => !row.IsNewRow)
                                        .Select(row => new
                                        {
                                            SubscriberNo = Convert.ToInt64(row.Cells[0].Value),
                                            CollectedAmount = Convert.ToDecimal(row.Cells[1].Value),
                                            PaymentDate = row.Cells[2].Value // 确保是DateTime类型,若为字符串请用DateTime.Parse转换
                                        })
                                        .ToList()
    };

    // 初始化UI状态
    progressBar1.Show();
    label6.Text = "0%";
    label7.Text = "";

    // 启动后台任务
    backgroundWorker1.RunWorkerAsync(updateParameters);
}

DoWork后台任务(核心数据库更新逻辑)

private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    var worker = sender as BackgroundWorker;
    var paramsData = e.Argument as dynamic; // 也可以定义一个强类型类,更规范
    int totalRows = paramsData.RowsToUpdate.Count;
    int successCount = 0;

    using (SqlConnection con = new SqlConnection("你的数据库连接字符串"))
    {
        con.Open();
        // 预先创建SQL命令和参数,避免循环中重复创建
        using (SqlCommand com = new SqlCommand(@"UPDATE indebtedness 
                                                SET collected = @collected,
                                                    Payment_Date = @Payment_Date,
                                                    Payment_Date2 = @Payment_Date2 
                                                WHERE Subscriber_No = @Subscriber_No 
                                                  AND company_name = @company_name 
                                                  AND indebtedness_name = @indebtedness_name", con))
        {
            com.Parameters.AddWithValue("@company_name", paramsData.CompanyName);
            com.Parameters.AddWithValue("@indebtedness_name", paramsData.IndebtednessName);
            com.Parameters.AddWithValue("@Payment_Date2", DateTime.Today);
            com.Parameters.Add("@Payment_Date", SqlDbType.Date);
            com.Parameters.Add("@Subscriber_No", SqlDbType.BigInt);
            com.Parameters.Add(new SqlParameter("@collected", SqlDbType.Decimal) { Precision = 18, Scale = 3 });

            for (int i = 0; i < totalRows; i++)
            {
                // 检查是否用户取消了任务(可选)
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                var rowData = paramsData.RowsToUpdate[i];
                // 更新参数值
                com.Parameters["@Subscriber_No"].Value = rowData.SubscriberNo;
                com.Parameters["@collected"].Value = rowData.CollectedAmount;
                com.Parameters["@Payment_Date"].Value = rowData.PaymentDate;

                // 执行更新
                int affectedRows = com.ExecuteNonQuery();
                successCount += affectedRows;

                // 计算并报告进度
                int progressPercent = (int)((i + 1) * 100.0 / totalRows);
                worker.ReportProgress(progressPercent, successCount); // 附带成功更新的行数

                // 可选:如果数据库操作太快,进度条闪得太快,可以加个短暂延迟
                // Thread.Sleep(10);
            }
        }
    }

    // 把成功行数作为结果返回给完成事件
    e.Result = successCount;
}

ProgressChanged进度更新事件

private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
    label6.Text = $"{e.ProgressPercentage}%";

    // 可选:显示已成功更新的行数
    int updatedRows = (int)e.UserState;
    // label8.Text = $"已更新 {updatedRows} 行";

    if (e.ProgressPercentage == 100)
    {
        label7.Text = "记录更新成功...";
    }
}

RunWorkerCompleted任务完成事件

private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        // 处理后台任务中的异常
        MessageBox.Show($"更新出错:{e.Error.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    else if (e.Cancelled)
    {
        label7.Text = "更新任务已取消";
    }
    else
    {
        int totalSuccess = (int)e.Result;
        MessageBox.Show($"成功更新 {totalSuccess} 行!", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    // 可选:隐藏进度条或重置状态
    // progressBar1.Hide();
}

这样调整后,不仅解决了重复点击的报错问题,还真正实现了后台批量更新+实时进度条的功能,UI也不会卡顿了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:10:32