2017-10-29 92 views
-1

這是我第一次工作woth一個並行for循環,我理解的基本知識,你可以看到我的代碼如下,但我不理解如何使內部變量循環線程安全。使用線程安全變量與並行for循環C#

我下面的文章在https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-write-a-parallel-for-loop-with-thread-local-variables

目前,我不斷得到錯誤爲:序列包含在我的計算類中沒有的元素時,它正在執行對數據的計算。我是否缺少一些簡單的東西來使所有線程安全?

更新:我添加了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; } 
} 

enter image description here

+0

評論不適用於擴展討論;這個對話已經[轉移到聊天](http://chat.stackoverflow.com/rooms/157820/discussion-on-question-by-user-3610374-using-a-thread-safe-variable-with-a-parall) 。 – Andy

回答

2

序列不包含任何元素

對於這個問題,該問題是與(在它沒有數據,即一組)服用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)。該?? 0null轉換爲0,如果沒有匹配(變化0到任何價值是有道理的,你的目的。

一種不同的方法,考慮https://github.com/morelinq/MoreLINQ/issues/28

您需要更改每項在switch報表以類似的方式來解決這個問題(與Close是要麼CloseHighLow,並與OrderByDescending是要麼OrderByDescendingOrderBy)。

此外,在您的原始代碼中,您正在執行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作爲結果(以線程安全的方式)。