使用线程安全变量与并行for循环C#
这是我第一次工作woth一个并行for循环,我理解的基本知识,你可以看到我的代码如下,但我不理解如何使内部变量循环线程安全。使用线程安全变量与并行for循环C#
目前,我不断得到错误为:序列包含在我的计算类中没有的元素时,它正在执行对数据的计算。我是否缺少一些简单的东西来使所有线程安全?
更新:我添加了Calculations类的所有相关代码,其中显示了一个方法作为返回常量的示例。Sequence没有包含任何元素异常,并且目前为止我尝试修复此问题(异常仍在进行中)
更新2:我在我的代码中添加了自定义类,它应该允许它现在编译。
public static async Task Test()
{
Vector<double> vectorArrayBuy = null;
Vector<double> vectorArraySell = null;
Calculations calcTemp = null;
try
{
using (financeEntities context = new financeEntities())
{
List<string> symbolList = new List<string>();
symbolList = GetStockSymbols("nasdaq");
foreach (string symbol in symbolList)
{
var query = await context.DailyStockDatas.Where(i => i.Symbol == symbol && i.Market == "nasdaq").ToListAsync();
if (query.Count >= 200)
{
List<MultipleRegressionInfo> listMRInfo = new List<MultipleRegressionInfo>();
Calculations calc = new Calculations(query, j);
calcTemp = calc;
Parallel.For(0, 200, j =>
{
var targetValueBuy = calc.ListCalculationData.Select(i => i.MRTargetValueBuy).ToList();
var targetValueSell = calc.ListCalculationData.Select(i => i.MRTargetValueSell).ToList();
vectorArrayBuy = CreateVector.Dense(targetValueBuy.ToArray());
vectorArraySell = CreateVector.Dense(targetValueSell.ToArray());
var name = calc.ListCalculationData.First();
IEnumerable<double> value;
value = calc.ListCalculationData.Select(i => i.WilliamsR);
MultipleRegressionInfo r1 = Rn(value, vectorArrayBuy, nameof(name.WilliamsR), j, calc);
listMRInfo.Add(r1);
});
class Calculations
{
public List<DailyStockData> Data { get; set; }
public ConcurrentBag<CalculationData> ListCalculationData { get; set; }
public Calculations(List<DailyStockData> dailyData, int days)
{
lock (thisLock)
{
Data = dailyData;
// initiate the data
ListCalculationData = new ConcurrentBag<CalculationData>();
for (int i = 0; i < Data.Count; i++)
{
var currentDate = Data.ElementAt(i).Date;
CalculationData calc = new CalculationData(currentCalcData);
calc.WilliamsR = CalculateWilliamsR(days, currentDate);
// add current calculator class to the list
ListCalculationData.Add(calc);
}
}
}
public double CalculateWilliamsR(int days, DateTime startingDate)
{
double williamsR = 0;
double highestHigh = 0;
double currentClose = 0;
double lowestLow = 0;
try
{
highestHigh = FindMaxOrMin(days, startingDate, MaxOrMinType.HighestHigh);
lowestLow = FindMaxOrMin(days, startingDate, MaxOrMinType.LowestLow);
currentClose = (double)Data.Where(i => i.Date <= startingDate).Last().Close;
williamsR = -100 * ((highestHigh - currentClose)/(highestHigh - lowestLow));
}
catch (Exception ex)
{
williamsR = 0;
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
return williamsR;
}
public enum MaxOrMinType
{
HighestHigh,
LowestLow,
HighestClose,
LowestClose
}
public double FindMaxOrMin(int days, DateTime startingDate, MaxOrMinType type)
{
double maxMin = 0;
try
{
lock (thisLock)
{
switch (type)
{
// gets Sequence contains no elements exceptions at all of the below lines
case MaxOrMinType.HighestClose:
maxMin = (double)Data.Where(i => i.Date <= startingDate).Take(days).Max(i => i.Close);
break;
case MaxOrMinType.HighestHigh:
maxMin = (double)Data.Where(i => i.Date <= startingDate).Take(days).Max(i => i.High);
break;
case MaxOrMinType.LowestClose:
maxMin = (double)Data.Where(i => i.Date <= startingDate).Take(days).Min(i => i.Close);
break;
case MaxOrMinType.LowestLow:
maxMin = (double)Data.Where(i => i.Date <= startingDate).Take(days).Min(i => i.Low);
break;
default:
break;
}
}
}
catch (Exception ex)
{
maxMin = 0;
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
return maxMin;
}
public class DailyStockData
{
public DailyStockData();
public int ID { get; set; }
public string Symbol { get; set; }
public string Market { get; set; }
public DateTime Date { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public decimal AdjustedClose { get; set; }
public long Volume { get; set; }
}
public class CalculationData
{
public CalculationData(CalculationData calcData)
{
Date = calcData.Date;
Open = calcData.Open;
High = calcData.High;
Low = calcData.Low;
Close = calcData.Close;
AdjustedClose = calcData.AdjustedClose;
Volume = calcData.Volume;
WilliamsR = calcData.WilliamsR;
}
public CalculationData() { }
public DateTime Date { get; set; }
public double Open { get; set; }
public double High { get; set; }
public double Low { get; set; }
public double Close { get; set; }
public double AdjustedClose { get; set; }
public double Volume { get; set; }
public double WilliamsR { get; set; }
}
序列不包含任何元素
对于这个问题,该问题是与(在它没有数据,即一组)服用Max
一个empty set的。因此:
失败。为了解决这个问题,将其更改为:
maxMin = Data.Where(i => i.Date <= startingDate).Take(days)
.OrderByDescending(z => z.Close)
.Select(z => (double?)z.Close)
.FirstOrDefault() ?? 0;
的OrderByDescending
将确保最高Close
列在第一位。 Select
将确保返回Close
值(或者如果根本没有条目,则返回null
)。该?? 0
将null
转换为0,如果没有匹配(变化0到任何价值是有道理的,你的目的。
一种不同的方法,考虑https://github.com/morelinq/MoreLINQ/issues/28。
您需要更改每项在switch
报表以类似的方式来解决这个问题(与Close
是要么Close
或High
或Low
,并与OrderByDescending
是要么OrderByDescending
或OrderBy
)。
此外,在您的原始代码中,您正在执行Take
而不是早期的OrderBy
,但现在我会忽略它。
线程安全
在呼吁listMRInfo.Add
的线程安全性方面 - 而不是做:
List<MultipleRegressionInfo> listMRInfo = new List<MultipleRegressionInfo>();
Parallel.For(0, 200, j =>
{
// Code here
listMRInfo.Add(r1);
});
考虑做:
var listMRInfo = Enumerable.Range(0, 200)
.AsParallel()
.Select(j =>
{
// Code here
return r1;
})
.ToList();
这将使你有相同的基本Parallel
行为为For
,但也允许您有一个List
作为结果(以线程安全的方式)。
评论不适用于扩展讨论;这个对话已经[转移到聊天](http://chat.stackoverflow.com/rooms/157820/discussion-on-question-by-user-3610374-using-a-thread-safe-variable-with-a-parall) 。 – Andy