0

I am sporadically seeing the following error,

System.IndexOutOfRangeException: Index was outside the bounds of the array. at System.Collections.Generic.List`1.Add(T item)

I have a console application which connects to multiple data sources and generate report in excel format.

In order to speedup the process, I am using Parallel.Invoke and it saved approx 40% of time.

The code structure is as follows,

public static void Execute(List<Members> activeRecords)
    {
        var resultList = new List<Recon>();

        var lookupList = DataManager.GetLookUpData();
        
        Parallel.Invoke(
             () =>
             {
                 GenerateReportA(activeRecords, lookupList, resultList);
             },
             () =>
             {
                 GenerateReportB(activeRecords, lookupList, resultList);
             },
             () =>
             {
                 GenerateReportC(activeRecords, lookupList, resultList);
             },
             () =>
             {
                 GenerateReportD(activeRecords, lookupList, resultList);
             },

And each GenerateReport method has similar structure but, generates different reports based on the need. There are totally 32 reports.

private static void GenerateReportA(List<Members> activeData, List<Recon> resultList)
    {
        var message = string.Empty;
        var reportName = $"{area} {Name}";
        var reportDataList = null;

        try
        {
            //logic which compares generates report data                
        }
        catch (Exception ex)
        {
            message = $"Error: {ex.Message}";
        }

        AddToResult(reportName, reportDataList, message, resultList);
    }

The issue is happening in AddToResult method at resultList.Add(recon);

private static void AddToResult(string reportName, IList data, string message, List<Recon> resultList)
    {
        var recon = new Recon
        {
            ReportName = reportName,
            ExcelData = ExcelManager.GetExcelData(reportName, message, data)
        };
        resultList.Add(recon);
    }

Need suggestion / guidance on how I can avoid this error but still using parallel.invoke. Any suggestion will be greatly appreciated.

JGV
  • 5,037
  • 9
  • 50
  • 94
  • As a side note, I would advise to switch from `Parallel.Invoke` to `Parallel.ForEach`, which has a better behavior [in case of failures](https://stackoverflow.com/questions/10867332/are-parallel-invoke-and-parallel-foreach-essentially-the-same-thing/69152942#69152942). – Theodor Zoulias Jan 20 '22 at 19:20

2 Answers2

2

The fundamental issue is that List<T>.Add is not thread safe. By trying to add to the same array in parallel, you are most likely causing an internal collision, potentially during resizing operations and it is leading to failed indexing.

The proper solution is to lock before adding:

private static object _locker = new object();

private static void AddToResult(string reportName, IList data, string message, List<Recon> resultList)
{
    var recon = new Recon
    {
        ReportName = reportName,
        ExcelData = ExcelManager.GetExcelData(reportName, message, data)
    };

    lock(_locker)
    {
        resultList.Add(recon);
    }
}
David L
  • 32,885
  • 8
  • 62
  • 93
1

If you change something in threads, make sure that no one can do that at the same time. In your case it easy, just add lock on list.

private static void AddToResult(string reportName, IList data, string message, List<Recon> resultList)
{
    var recon = new Recon
    {
        ReportName = reportName,
        ExcelData = ExcelManager.GetExcelData(reportName, message, data)
    };

    lock (resultList)
    {
        resultList.Add(recon);
    }
}

Also ensure that your code do not change data list.

Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32