将线程安全变量与并行循环 C# 结合使用
Using a thread safe variable with a parallel for loop C#
这是我第一次使用并行 for 循环,我了解基础知识,正如您在下面的代码中看到的那样,但我不了解如何使循环线程内的变量安全。
上的文章
我目前不断收到以下错误:在对数据执行计算时,序列在我的计算 class 中不包含任何元素。我是否遗漏了一些让所有线程安全的简单方法?
更新:我为计算添加了所有相关代码 class,以一个方法为例,该方法返回常量序列不包含任何元素异常以及我到目前为止为尝试解决该问题所做的工作(例外情况仍在继续)
更新 2:我在我的代码中添加了自定义 classes,现在应该可以编译了。
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; }
}
Sequence contains no elements
对于这个问题,问题在于获取 empty set 的 Max
(即其中没有数据的集合)。因此:
maxMin = (double)Data.Where(i => i.Date <= startingDate).Take(days).Max(i => i.Close);
失败。要解决此问题,请将其更改为:
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
)。
此外,在您的原始代码中,您在没有更早的 OrderBy
的情况下执行 Take
很奇怪,但我暂时忽略它。
Thread safety
就调用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();
这将允许您拥有与 For
相同的基本 Parallel
行为,但也允许您拥有 List
作为结果(以线程安全方式)。
这是我第一次使用并行 for 循环,我了解基础知识,正如您在下面的代码中看到的那样,但我不了解如何使循环线程内的变量安全。
上的文章我目前不断收到以下错误:在对数据执行计算时,序列在我的计算 class 中不包含任何元素。我是否遗漏了一些让所有线程安全的简单方法?
更新:我为计算添加了所有相关代码 class,以一个方法为例,该方法返回常量序列不包含任何元素异常以及我到目前为止为尝试解决该问题所做的工作(例外情况仍在继续)
更新 2:我在我的代码中添加了自定义 classes,现在应该可以编译了。
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; }
}
Sequence contains no elements
对于这个问题,问题在于获取 empty set 的 Max
(即其中没有数据的集合)。因此:
maxMin = (double)Data.Where(i => i.Date <= startingDate).Take(days).Max(i => i.Close);
失败。要解决此问题,请将其更改为:
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
)。
此外,在您的原始代码中,您在没有更早的 OrderBy
的情况下执行 Take
很奇怪,但我暂时忽略它。
Thread safety
就调用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();
这将允许您拥有与 For
相同的基本 Parallel
行为,但也允许您拥有 List
作为结果(以线程安全方式)。