【问题标题】:Source array was not long enough. Check srcIndex and length, and the array's lower bounds源数组不够长。检查 srcIndex 和长度,以及数组的下限
【发布时间】:2020-01-07 04:12:10
【问题描述】:

我有一个 C# 列表,它将在 Parallel Foreach 中增加价值。现在它总是返回异常 System.IndexOutOfRangeException。当我指向 listTotalCost 时,它有以下消息

源数组不够长。检查 srcIndex 和长度,以及 数组的下界。

它是由线程安全问题和其他问题引起的吗?这是我的代码

List<decimal> listTotalCost = new List<decimal>();

Parallel.ForEach(listDates, dates =>
{
    using (DataSet result = calculationMgr.EvaluateFormula(companyID, dates.startDate, dates.endDate, subIndicatorID.Value.ToString(), null, false, null
        , (int)Common.Systems.Sustainability.Constants.ApprovalStatuses.Approved
        ))
    {
        DataRow dr = result.Tables[0].Rows[0];
        //totalPrice = Convert.ToDecimal(dr["Result"]).ToString("#,##0.00");
        decimal? temp = Common.Util.TryToConvertToDecimal(dr, "Result");
        if (temp != null)
        {
            //the following line is the error happened 
            listTotalCost.Add(temp.Value);
        }
    }
});

【问题讨论】:

  • 您正在执行Parallel 循环并在非线程安全上下文中添加值。像这样修改变量时要非常小心。
  • 你应该考虑采取thread safe collections之一可能是ConcurrentBag
  • 这是可能的原因吗:这两件事都必须以原子方式完成。在您的代码中,可能会发生两个线程在索引 5 处添加一个新元素并且两者都在增加的情况。结果,您可能在索引 5 处有一个元素,但在索引 6 处没有!这种情况不太可能发生,但它可能会发生......我该如何避免它?
  • 使用线程安全的集合或锁定对列表的访问。

标签: c# multithreading exception


【解决方案1】:

您正在通过不同的线程访问列表:

您可以使用以下命令锁定列表:

lock(listTotalCost)
    listTotalCost.Add(temp.Value);

或使用Concurrent collections

【讨论】:

    【解决方案2】:

    除了使用Parallel.ForEach,您还可以使用PLINQ(并行LINQ),并在最后调用ToList()。它将为您处理排序和线程同步。

    var listTotalCost = listDates
        .AsParallel() // this makes it parallel
        .AsOrdered() // optional
        .WithDegreeOfParallelism(2) // optional
        .Select(date =>
        {
            using (DataSet result = calculationMgr.EvaluateFormula(companyID,
                date.startDate, date.endDate, subIndicatorID.Value.ToString(),
                null, false, null,
                (int)Common.Systems.Sustainability.Constants.ApprovalStatuses.Approved))
            {
                DataRow dr = result.Tables[0].Rows[0];
                return Common.Util.TryToConvertToDecimal(dr, "Result");
            }
        })
        .Where(v => v != null)
        .Select(v => v.Value)
        .ToList();
    

    【讨论】:

      【解决方案3】:

      使用来自System.Collections.Concurrent 命名空间的东西,你可能想要ConcurrentBag&lt;T&gt;。请注意,它不保证订购。

      【讨论】:

        【解决方案4】:

        您可以在此处使用 select 语句应用异步等待方法。你只需要改变你的方法来返回值。在额外的方法中提取代码:

        private async Task<decimal?> DoItAsync(yourType dates)
        {
            return await Task.Run(()=> 
            {
                using (DataSet result = calculationMgr.EvaluateFormula(companyID, dates.startDate, dates.endDate, subIndicatorID.Value.ToString(), null, false, null
                , (int)Common.Systems.Sustainability.Constants.ApprovalStatuses.Approved
                ))
            {
                DataRow dr = result.Tables[0].Rows[0];
                //totalPrice = Convert.ToDecimal(dr["Result"]).ToString("#,##0.00");
                return Common.Util.TryToConvertToDecimal(dr, "Result");        
            }
           });
        }
        

        然后执行将并行执行的select,等待所有返回的任务,抓取结果并过滤掉没有值的:

        List<decimal> listTotalCost = Task.WhenAll(listDates.Select(async x => await DoItAsync(x)))
                                    .Result
                                    .Where(x => x.HasValue)
                                    .Select(x => x.Value)
                                    .ToList(); 
        

        这种方法将为您创建一个集合,而不是并行收集每个元素。顺序会打乱,但这是正常的,在并行处理时应该预料到

        【讨论】:

          【解决方案5】:

          除了其他修复选项之外,将 listTotalCost 设置为 ConcurrentBag 也应该修复它(正如 Jeroen 建议的那样)。问题不在于您要从中添加的数组中的“源”数组,正如异常可能暗示的那样。

          Parallel.ForEach 执行后,您可以.ToList().ToArray() listTotalCost 将其返回。

          【讨论】:

            猜你喜欢
            • 2013-08-29
            • 2016-04-08
            • 2022-01-23
            • 1970-01-01
            • 2019-09-12
            • 1970-01-01
            • 2020-03-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多