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

多线程场景下DataTable.ToList()抛出集合已修改异常的解决方案咨询

问题描述

我的应用通过多线程修改某一DataTable,已为添加行和提交数据库的操作实现独占锁机制(由两个不同线程处理)。现UI线程(第三个线程)需使用该表通过LINQ筛选数据并绑定到Chart控件。我已将操作拆分为最小指令以定位问题:

var s1 = dataTable.ToList(); //originally it was dataTable.ToEnumerable()
var s2 = s1.Select(LINQ_expr1);
var s3 = s2.Where(LINQ_expr2);
var s4 = s3.OrderBy(LINQ_expr3);

异常始终发生在执行第一条指令时。目前我能想到的唯一可行方案是在此处加入锁机制,但希望避免该方案,寻求可创建变量副本且能避免此异常的替代方法。该表可能包含数十万行(会定期截断,但仅在分析完成后),因此希望找到比DataTable.Copy()开销更低的替代锁机制的方案。

可行方案

1. 只复制LINQ所需字段到POCO列表

直接放弃复制整个DataTable,手动遍历DataRow,只提取后续LINQ查询需要用到的字段,存入自定义的简单POCO类列表中。这种方式内存开销远低于复制整个DataTable,且单条DataRow的读取是线程安全的(只要写入线程只添加新行,不修改已有行):

// 定义匹配LINQ查询需求的POCO类
public class DataItem
{
    public int Value { get; set; }
    public DateTime Timestamp { get; set; }
    // 仅保留需要的字段
}

// UI线程读取数据
var s1 = new List<DataItem>();
foreach (DataRow row in dataTable.Rows)
{
    // 仅复制必要字段,减少冗余数据
    s1.Add(new DataItem
    {
        Value = row.Field<int>("Value"),
        Timestamp = row.Field<DateTime>("Timestamp")
    });
}

// 后续LINQ操作基于这个精简列表
var s4 = s1.Select(LINQ_expr1).Where(LINQ_expr2).OrderBy(LINQ_expr3);

2. 短锁创建DataView视图

DataView是基于DataTable的轻量级视图,仅在创建DataView的瞬间加锁,锁的持有时间极短,对性能影响可以忽略。后续LINQ直接基于DataView的枚举操作,无需再锁:

IEnumerable<DataRowView> dataView;
// 仅在创建DataView时加锁,避免长时间阻塞写入线程
lock (yourExistingLockObject)
{
    dataView = new DataView(dataTable, "", "", DataViewRowState.CurrentRows).Cast<DataRowView>();
}

// 后续LINQ操作基于这个视图枚举
var s4 = dataView.Select(LINQ_expr1).Where(LINQ_expr2).OrderBy(LINQ_expr3);

3. 生产者-消费者模式维护只读副本

让写入线程在添加新行的同时,把该行的必要数据同步到一个线程安全的只读集合(比如ConcurrentBag<T>)。UI线程直接查询这个集合,完全不需要锁,也不需要复制整个DataTable:

// 全局线程安全集合,仅由写入线程添加数据
private readonly ConcurrentBag<DataItem> _readOnlyData = new ConcurrentBag<DataItem>();

// 写入线程添加行时同步数据
lock (yourInsertLock)
{
    var newRow = dataTable.NewRow();
    // 填充行数据
    newRow["Value"] = 123;
    newRow["Timestamp"] = DateTime.Now;
    dataTable.Rows.Add(newRow);
    
    // 同步必要字段到只读集合
    _readOnlyData.Add(new DataItem
    {
        Value = (int)newRow["Value"],
        Timestamp = (DateTime)newRow["Timestamp"]
    });
}

// UI线程直接查询只读集合,无锁开销
var s4 = _readOnlyData.Select(LINQ_expr1).Where(LINQ_expr2).OrderBy(LINQ_expr3);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 15:48:24