C#多线程MySQL备份下自增变量绑定DataGridView同步更新进度实现问询
实现方案
你遇到的问题核心是多线程共用实例导致属性覆盖、foreach闭包捕获异常、进度回调无对应行标识三个问题,以下是可直接落地的修改方案:
1. 核心逻辑说明
- 每个线程对应独立的
mysqlUtils实例,避免多线程并发修改同一个实例的属性 - 用
Interlocked.Increment实现线程安全的自增序号,保证每个线程拿到唯一的行索引 - 解决foreach循环变量的闭包捕获问题,避免所有线程拿到最后一行的参数
- 跨线程更新UI时通过
Invoke封送到UI线程执行,避免控件操作异常
2. Form.cs 代码修改
private void button1_Click(object sender, EventArgs e) { restoreUI(); } public void restoreUI() { // 初始化自增序号,初始值-1,第一次自增后得到0,符合第一个线程序号为0的要求 int datagridIterate = -1; foreach(DataGridViewRow row in dataGridView1.Rows) { // 跳过DataGridView的空白新行模板 if (row.IsNewRow) continue; // 每个线程创建独立的mysqlUtils实例,避免属性互相覆盖 mysqlUtils msu = new mysqlUtils(); // 线程安全自增,拿到当前线程对应的行号 int currentIterate = Interlocked.Increment(ref datagridIterate); // 将行号绑定到当前mysqlUtils实例 msu.RowIndex = currentIterate; // 解决foreach闭包问题:创建局部变量存储当前行,不要直接使用循环变量row DataGridViewRow currentRow = row; Thread t = new Thread(() => { msu.Restore(currentRow.Cells[1].Value?.ToString(), server.Text, username.Text, pwd.Text, currentRow.Cells[0].Value?.ToString()); }); t.Start(); } } // 新增UI进度更新方法,处理跨线程更新逻辑 public void updateProgressValue(int rowIndex, int total, int cur) { if (dataGridView1.InvokeRequired) { // 跨线程时自动封送到UI线程执行 dataGridView1.Invoke(new Action(() => updateProgressValue(rowIndex, total, cur))); return; } // 行号合法时更新对应单元格内容,可根据需要自定义显示格式 if (rowIndex >= 0 && rowIndex < dataGridView1.Rows.Count) { dataGridView1.Rows[rowIndex].Cells[2].Value = $"已完成:{cur * 100 / total}% ({cur}/{total}字节)"; } }
3. Class1.cs 代码修改
namespace Utils { public class mysqlUtils { // 新增行号属性,存储当前实例对应的DataGridView行索引 public int RowIndex { get; set; } public int total; public int cur; public void Restore(string filename, string server, string user, string password, string db) { string constring = "server=" + server + ";user=" + user + ";pwd=" + password + ";database=" + db + ";"; string file = "C:\\Users\\SQL FILES\\" + filename; using (MySqlConnection conn = new MySqlConnection(constring)) { using (MySqlCommand cmd = new MySqlCommand()) { using (MySqlBackup mb = new MySqlBackup(cmd)) { cmd.Connection = conn; conn.Open(); mb.ImportInfo.IntervalForProgressReport = 50; mb.ImportProgressChanged += updateProgress; mb.ImportFromFile(file); conn.Close(); } } } } // 修正:将updateProgress作为mysqlUtils的实例方法,绑定当前实例的行号 private void updateProgress(object sender, ImportProgressArgs e) { total = (int)e.TotalBytes; cur = (int)e.CurrentBytes; Form4 form4 = (Form4)Application.OpenForms[0]; // 传递当前实例对应的行号到Form的更新方法 form4.updateProgressValue(RowIndex, total, cur); } } }
内容的提问来源于stack exchange,提问作者Dat Cong Dinh
相关产品推荐
相关产品推荐

