Parallel.Invoke和Index超出了数组的界限



我偶尔会看到以下错误,

System.IndexOutOfRangeException:索引超出大堆位于System.Collections.Generic.List`1.添加(T项(

我有一个控制台应用程序,它连接到多个数据源并以excel格式生成报告。

为了加快进程,我使用Parallel.Invoke,它节省了大约40%的时间。

代码结构如下,

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

每个GenerateReport方法都有相似的结构,但根据需要生成不同的报告。共有32份报告。

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

问题发生在resultList.Add(reco(;的AddToResult方法中;

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

需要建议/指导我如何避免这个错误,但仍然使用parallel.invoke。任何建议都将不胜感激。

根本问题是List<T>.Add不是线程安全的。通过尝试并行添加到同一个数组,很可能会导致内部冲突,可能是在调整大小操作期间,并导致索引失败。

正确的解决方案是在添加之前锁定:

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

如果更改线程中的某些内容,请确保没有人可以同时执行。在您的情况下,只需将lock添加到列表中即可。

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

还要确保您的代码不会更改data列表。

最新更新