【问题标题】:Parallel.Invoke and Index was outside the bounds of the array [duplicate]Parallel.Invoke 和 Index 超出了数组的范围[重复]
【发布时间】:2022-01-21 01:59:53
【问题描述】:

我偶尔会看到以下错误,

System.IndexOutOfRangeException:索引超出范围 大批。在 System.Collections.Generic.List`1.Add(T item)

我有一个控制台应用程序,它连接到多个数据源并生成 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(recon) 的 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 的建议/指导。任何建议将不胜感激。

【问题讨论】:

  • 附带说明,我建议从Parallel.Invoke 切换到Parallel.ForEach,它的行为更好in case of failures

标签: c# multithreading linq thread-safety generic-list


【解决方案1】:

根本问题是List&lt;T&gt;.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);
    }
}

【讨论】:

    【解决方案2】:

    如果您更改线程中的某些内容,请确保没有人可以同时执行此操作。在您的情况下很容易,只需在列表中添加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 列表。

    【讨论】:

      猜你喜欢
      • 2011-04-11
      • 2012-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多