如何解决使用Parallel.For搜索时dataGridView1跨线程访问错误
问题原因分析
- 死锁根源:
Parallel.For是同步阻塞方法,直接在UI线程的inputSearch_TextChanged事件中执行时,会阻塞当前UI线程等待所有并行任务完成,而并行任务里的Invoke又需要等待UI线程空闲才能执行,双方互相等待就形成死锁。 - 未处理的跨线程访问:并行任务中直接读取
inputSearch.Text属性,这也是跨线程访问UI控件的操作,同样会触发跨线程异常,原代码只处理了修改DataGridView的部分,漏了读取搜索框内容的操作。 - 无效重复刷新:7个并行任务每个匹配到结果都会单独更新
DataGridView数据源,不仅会导致多次无意义的UI刷新,还可能出现后执行的任务覆盖先执行任务结果的逻辑问题。
修复后的代码
private void inputSearch_TextChanged(object sender, EventArgs e) { // 先在UI线程提前取好所有需要的参数,避免并行任务中跨线程访问控件 string searchValue = inputSearch.Text.ToUpper(); // 把并行逻辑扔到线程池执行,不阻塞UI线程 Task.Run(() => { List<DataRow> resultRows = new List<DataRow>(); // 并行搜索所有列,把结果汇总 Parallel.For(0, 7, i => { var matched = dataTable.AsEnumerable() .Where(row => row[i].ToString().Contains(searchValue)) .ToList(); // 加锁避免多线程操作集合冲突 lock (resultRows) { resultRows.AddRange(matched); } }); // 统一更新UI Invoke(new Action(() => { if (resultRows.Any()) { // 去重避免同一条数据匹配多列重复显示 dataGridView1.DataSource = resultRows.Distinct().CopyToDataTable(); dataGridView1.Columns[7].Visible = false; } else { dataGridView1.DataSource = null; } })); }); }
额外优化建议
- 如果输入频率很高,建议加防抖逻辑,比如用户停止输入300ms后再执行搜索,避免频繁触发并行搜索浪费资源。
- DataTable本身不大的情况下,并行搜索的收益可能不如直接单线程遍历所有列,多线程上下文切换的开销可能超过并行收益,可以根据实际数据量测试调整。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

