Source array was not long enough. Check srcIndex and length, and the array's lower bounds

11,680

Solution 1

You're accessing the list by different threads:

You could lock the list with:

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

Or use Concurrent collections.

Solution 2

Instead of using Parallel.ForEach you could also use PLINQ (Parallel LINQ), and call ToList() at the end. It will take care of the ordering and the thread synchronization for you.

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();

Solution 3

you could apply here a async await approach using a select statement. You just need to change your method to return the value. Extract the code in an extra method:

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");        
    }
   });
}

Then perform the select which will be executed in parallel, wait for all the returned tasks, grab the results and filter out the ones which have no value:

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

This approach will create for you a collection instead of collecting each element bit by bit in parallel. The order will be messed up, but this is normal and should be expected when handling things in parallel

Solution 4

Use something from the System.Collections.Concurrent namespace, you probably want ConcurrentBag<T>. Note that it does not guarantee ordering.

Share:
11,680

Related videos on Youtube

want_to_be_calm
Author by

want_to_be_calm

Updated on June 04, 2022

Comments

  • want_to_be_calm
    want_to_be_calm almost 2 years

    I have a C# list which will be added value in Parallel Foreach. Now it always returns exception System.IndexOutOfRangeException. When I pointed to the listTotalCost, it has the following message

    Source array was not long enough. Check srcIndex and length, and the array's lower bounds.

    Is it caused by the thread safe problem and any other issue? Here is my code

    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);
            }
        }
    });
    
    • Blue
      Blue over 4 years
      You're doing a Parallel loop and adding values in a non-thread safe context. Be VERY careful when modifying variables like this.
    • Mong Zhu
      Mong Zhu over 4 years
      you should consider to take one of the thread safe collections may be a ConcurrentBag
    • want_to_be_calm
      want_to_be_calm over 4 years
      Is this the possible reason: Both things have to be done atomically. Within your code, it may happen, that two threads are adding a new element at index 5 and both are increasing. As a result you may have one element at index 5 but none at index 6! This scenario isn't very probable, but it MAY happen...how can I avoid it?
    • Lasse V. Karlsen
      Lasse V. Karlsen over 4 years
      Use a thread-safe collection or lock access to the list.