将类添加到列表C#时,索引超出了数组的范围

时间:2015-02-04 02:09:10

标签: c# sql exception exception-handling parallel-processing

异常:索引超出了数组的范围。

首先,我熟悉这个异常并且我之前已经修复了它但是我在代码中的一个非常奇怪的行上得到了这个异常。当我将用户创建的类添加到我的代码中的类列表时,它被抛出。我完全迷失了它为什么抛出这个异常以及如何修复它。

public static async Task getData()
    {
        // initialize everything
        List<StockData> stockData = new List<StockData>();
        List<StockMarketCompare> stockCompareData = new List<StockMarketCompare>();
        List<StockData> sandpInfo = new List<StockData>();
        List<StockData> sandpDateInfo = new List<StockData>();
        List<StockData> amexList = new List<StockData>();
        List<DateTime> completedDates = new List<DateTime>();
        SymbolInfo symbolClass = new SymbolInfo();
        List<SymbolInfo> ratingSymbols = new List<SymbolInfo>();
        List<StockRating> ratingList = new List<StockRating>();
        bool isGoodToGo = false;
        string symbol, market;
        int activeSymbolsCount = 0;
        int rowCount = 0, completedRowCount = 0;
        DateTime date = new DateTime();
        DateTime searchDate = new DateTime();

        using (SqlConnection connection = new SqlConnection("connectionstring"))
        using (SqlCommand sandpCommand = new SqlCommand("select * from dbo.DailyGlobalData where Symbol='" + Calculations.sp500 + "'", connection))
        using (SqlDataAdapter sandpAdapter = new SqlDataAdapter(sandpCommand))
        using (DataTable sandpTable = new DataTable("sandp"))
        using (SqlCommand stockRatingsCommand = new SqlCommand("select * from dbo.StockRatings", connection))
        using (SqlDataAdapter stockRatingsAdapter = new SqlDataAdapter(stockRatingsCommand))
        using (DataTable stockRatingsTable = new DataTable("stockratings"))
        {
            try
            {
                // fill the sandptable
                sandpAdapter.Fill(sandpTable);

                if (sandpTable != null)
                {
                    var sandpQuery = from c in sandpTable.AsEnumerable()
                                     select new StockData { Close = c.Field<decimal>("Close"), Date = c.Field<DateTime>("Date"), High = c.Field<decimal>("High"), Low = c.Field<decimal>("Low"), Volume = c.Field<Int64>("Volume") };
                    sandpInfo = sandpQuery.AsParallel().ToList();
                }

                // fill the stockratingstable
                stockRatingsAdapter.Fill(stockRatingsTable);

                if (stockRatingsTable != null)
                {
                    activeSymbolsCount = stockRatingsTable.Rows.Count;

                    var symbolsAmountQuery = from c in stockRatingsTable.AsEnumerable()
                                             select new SymbolInfo { Symbol = c.Field<string>("Symbol"), Market = c.Field<string>("Market") };
                    ratingSymbols = symbolsAmountQuery.AsParallel().ToList();
                }

                for (int i = 0; i < activeSymbolsCount; i++)
                {
                    symbol = ratingSymbols.AsParallel().ElementAtOrDefault(i).Symbol;
                    market = ratingSymbols.AsParallel().ElementAtOrDefault(i).Market;
                    ratingList = new List<StockRating>();

                    using (SqlCommand historicalRatingsCommand = new SqlCommand("select * from dbo.OldStockRatings where Symbol='" + symbol + "' and Market='" + market + "'", connection))
                    using (SqlDataAdapter historicalRatingsAdapter = new SqlDataAdapter(historicalRatingsCommand))
                    using (DataTable historicalRatingsTable = new DataTable("historicalratings"))
                    {
                        // fill the historical ratings table
                        historicalRatingsAdapter.Fill(historicalRatingsTable);

                        if (historicalRatingsTable != null)
                        {
                            completedRowCount = historicalRatingsTable.AsEnumerable().AsParallel().Count();
                            completedDates = historicalRatingsTable.AsEnumerable().AsParallel().Select(d => d.Field<DateTime>("Date")).ToList();
                        }
                    }
                            using (SqlCommand amexCommand = new SqlCommand("select * from dbo.DailyAmexData where Symbol='" + symbol + "'", connection))
                            using (SqlDataAdapter amexAdapter = new SqlDataAdapter(amexCommand))
                            using (DataTable amexTable = new DataTable("amexdata"))
                            {
                                // fill the amex data table
                                amexAdapter.Fill(amexTable);

                                if (amexTable != null)
                                {
                                    var amexFillQuery = from c in amexTable.AsEnumerable()
                                                        select new StockData { Close = c.Field<decimal>("Close"), Date = c.Field<DateTime>("Date"), High = c.Field<decimal>("High"), Low = c.Field<decimal>("Low"), Volume = c.Field<Int64>("Volume") };
                                    amexList = amexFillQuery.AsParallel().ToList();

                                    rowCount = amexList.AsParallel().Count();
                                }
                            }

                    Parallel.For(0, rowCount - 30, new ParallelOptions
                    {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    }, async j =>
                    {
                                if (amexList.AsParallel().Count() > 0)
                                {
                                    date = amexList.AsParallel().ElementAtOrDefault(j).Date;
                                    searchDate = date.Subtract(TimeSpan.FromDays(60));

                                    if (completedDates.Contains(date) == false)
                                    {
                                        var amexQuery = from c in sandpInfo
                                                        where c.Date >= searchDate && c.Date <= date
                                                        join d in amexList on c.Date equals d.Date
                                                        select new StockMarketCompare { stockClose = d.Close, marketClose = c.Close };

                                        var amexStockDataQuery = from c in amexList
                                                                 where c.Date >= searchDate && c.Date <= date
                                                                 select new StockData { Close = c.Close, High = c.High, Low = c.Low, Volume = c.Volume, Date = c.Date };

                                        stockCompareData = amexQuery.AsParallel().ToList();
                                        stockData = amexStockDataQuery.AsParallel().ToList();
                                        isGoodToGo = true;
                                    }
                                    else
                                    {
                                        isGoodToGo = false;
                                    }
                                }

                        if (completedDates.Contains(date) == false)
                        {
                            var sandpDateQuery = from c in sandpInfo
                                                 where c.Date >= searchDate && c.Date <= date
                                                 select c;
                            sandpDateInfo = sandpDateQuery.AsParallel().ToList();
                            symbolClass = new SymbolInfo() { Symbol = symbol, Market = market };
                            isGoodToGo = true;
                        }
                        else
                        {
                            isGoodToGo = false;
                        }

                        if (isGoodToGo)
                        {
                            StockRating rating = performCalculations(symbolClass, date, sandpInfo, stockData, stockCompareData);

                                if (rating != null)
                                {
                                    **ratingList.Add(rating);** // getting the exception thrown here
                                }
                        }
                    });

                    // now save the results to the table outside the parallel for loop

                    ratingList.RemoveAll(item => item == null);
                    List<StockRating> masterList = ratingList.DistinctBy(j => j.date).ToList();
                    saveToTable(masterList, symbol, market);
                    // close the connection
                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                // close the connection
                connection.Close();
            }
        }
    }

2 个答案:

答案 0 :(得分:6)

List<T>不是线程安全的,您正在.Add内调用Parallel.For。您需要锁定Add或使用System.Collections.Concurrent命名空间中的threadsafe集合。

这不是您唯一的线程错误,例如,您在Parallel.For内部分配了几个在循环外的范围内声明的变量。你的各种线程将分别写下这些值。

答案 1 :(得分:3)

ratingsList不是线程安全的,因为List<T>不保证是线程安全的(静态方法除外),但是你是从多个线程修改它。

  

此类型的公共静态(在Visual Basic中为Shared)成员是线程安全的。任何实例成员都不保证是线程安全的。   在List上执行多个读取操作是安全的,但如果在读取集合时修改了集合,则可能会出现问题。要确保线程安全,请在读取或写入操作期间锁定集合。要使多个线程可以访问集合以进行读写,您必须实现自己的同步。对于具有内置同步的集合,请参阅System.Collections.Concurrent命名空间中的类。有关本质上线程安全的替代方法,请参阅ImmutableList类。

https://msdn.microsoft.com/en-us/library/6sh2ey19%28v=vs.110%29.aspx

改为使用thread safe collection