异常:索引超出了数组的范围。
首先,我熟悉这个异常并且我之前已经修复了它但是我在代码中的一个非常奇怪的行上得到了这个异常。当我将用户创建的类添加到我的代码中的类列表时,它被抛出。我完全迷失了它为什么抛出这个异常以及如何修复它。
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();
}
}
}
答案 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