C# WPF应用运行时修改DataTable抛出System.ArgumentOutOfRangeException异常求助
问题根因
你的错误本质是DataTable多线程操作不安全+定时器重入,和行数正相关的报错延迟也完全匹配这个根因:
- 你用
Task.Run把ProcessToastMessages放到后台线程执行,同时DataTable绑定了UI的DataGrid,UI线程本身也会随时读取DataTable的状态; - 定时器间隔为10秒,当DataTable行数较多时,逐行Ping的总耗时很容易超过10秒,导致上一次任务还没跑完,下一次定时器触发又启动了一个新的后台任务,多个线程同时读写同一个DataTable,行计数状态会出现紊乱;
- 你提前缓存了
rowCount = ds.Tables[0].Rows.Count作为遍历边界,但遍历过程中如果DataTable行数被其他线程修改(新增/删除),缓存的计数和实际行数不一致,就会触发索引越界。
修复方案
1. 阻止定时器任务重入
加运行状态标志,确保同一时间只有一个ProcessToastMessages在执行:
private bool _isProcessing = false; private async void timer_Tick(object sender, EventArgs e) { // 上一次任务未执行完直接跳过本次 if (_isProcessing) return; _isProcessing = true; try { await Task.Run(() => ProcessToastMessages()); } finally { _isProcessing = false; } }
2. 安全遍历DataTable
不要提前缓存行数,遍历前先给当前DataTable的行拍快照,避免遍历过程中行数变化导致越界:
private void ProcessToastMessages() { WindowsToasts.WindowsToast windowsUpdateToast = new WindowsToast(); // 拍行快照,后续遍历快照不受DataTable实时变化影响 var rows = ds.Tables[0].Rows.Cast<DataRow>().ToList(); foreach (var row in rows) { DateTime scheduledTime = DateTime.Now; DateTime.TryParse(row["schedule"].ToString(), out scheduledTime); string currentStatus = row["status"].ToString(); if ((scheduledTime.Ticks < DateTime.Now.Ticks) && currentStatus == "Pending") { string computerName = row["hostname"].ToString(); bool isOnline = ComputerFuncs.ComputerOnline(computerName); // DataTable绑定了UI控件,修改操作要调度到UI线程执行,避免跨线程访问异常 Application.Current.Dispatcher.Invoke(() => { if (isOnline) { row.SetField("online", true); row.SetField("status", "Sent"); } else { row.SetField("online", false); } }); } } }
3. 额外优化建议
可以把同步Ping替换为异步Ping方法,减少线程阻塞时间,进一步降低任务超时重入的概率。
内容的提问来源于stack exchange,提问作者Mogash
相关产品推荐
相关产品推荐

