Parallel.ForEach处理List导致数据丢失

是同一个dt,但执行之后的结果行数不同,代码如下:

 List<logistics_freight_bill_tbl> insertList1 = new List<logistics_freight_bill_tbl>();
            foreach (DataRow row in dt.Rows)
            {
                logistics_freight_bill_tbl info = new logistics_freight_bill_tbl();
                info.address = row["目的地"].ToString();
                insertList1.Add(info);
            }

            List<logistics_freight_bill_tbl> insertList2 = new List<logistics_freight_bill_tbl>();
            Parallel.ForEach(dt.Rows.Cast<DataRow>(), new ParallelOptions() { MaxDegreeOfParallelism = 10 }, async (row) =>
            {
                logistics_freight_bill_tbl info = new logistics_freight_bill_tbl();
                info.address = row["目的地"].ToString();
                insertList2.Add(info);
            });

insertList1 是在一个普通的 foreach 循环中被填充,而 insertList2 是在一个并行循环 Parallel.ForEach 中被填充。问题出现在对 insertList2 的操作上,因为 List<T> 类型不是线程安全的。

当你在 Parallel.ForEach 中使用 insertList2.Add(info) 时,可能会有多个线程同时尝试向 insertList2 添加元素。由于 List<T> 没有同步机制来保护其内部数据结构,在并发环境下这可能导致数据丢失或异常,从而使得 insertList2 的最终元素数量与 dt.Rows 的行数不一致。

要解决这个问题,你可以采取以下几种方法之一:

1. 使用线程安全集合

例如,ConcurrentBag<T> 是一个线程安全的集合,适用于并行操作。

ConcurrentBag<logistics_freight_bill_tbl> insertList2 = new ConcurrentBag<logistics_freight_bill_tbl>();
Parallel.ForEach(dt.Rows.Cast<DataRow>(), new ParallelOptions() { MaxDegreeOfParallelism = 10 }, (row) =>
{
    logistics_freight_bill_tbl info = new logistics_freight_bill_tbl();
    info.address = row["目的地"].ToString();
    insertList2.Add(info);
});

2. 使用锁

通过在添加元素时使用锁来确保每次只有一个线程能够修改列表。

List<logistics_freight_bill_tbl> insertList2 = new List<logistics_freight_bill_tbl>();
object lockObj = new object();
Parallel.ForEach(dt.Rows.Cast<DataRow>(), new ParallelOptions() { MaxDegreeOfParallelism = 10 }, (row) =>
{
    logistics_freight_bill_tbl info = new logistics_freight_bill_tbl();
    info.address = row["目的地"].ToString();
    lock (lockObj)
    {
        insertList2.Add(info);
    }
});

3. 收集并行结果然后合并

在并行循环中,为每个线程创建局部列表,然后在循环结束后将它们合并到主列表中。

List<logistics_freight_bill_tbl> insertList2 = new List<logistics_freight_bill_tbl>();
Parallel.ForEach(dt.Rows.Cast<DataRow>(),
    new ParallelOptions() { MaxDegreeOfParallelism = 10 },
    () => new List<logistics_freight_bill_tbl>(), // 初始化线程局部变量
    (row, state, localList) => // body
    {
        logistics_freight_bill_tbl info = new logistics_freight_bill_tbl();
        info.address = row["目的地"].ToString();
        localList.Add(info);
        return localList;
    },
    (finalResult) => // localFinally
    {
        lock (insertList2)
        {
            insertList2.AddRange(finalResult);
        }
    }
);

在这些方法中,选择哪一种取决于你的具体需求和性能考虑。如果保持顺序不是必须的,ConcurrentBag<T> 是一个简单有效的选择。如果需要保持特定的顺序,那么使用锁或者局部集合的方法可能更合适。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值