2017-04-26 4 views
0

私は、いくつかの並列データ処理とMySQLの使用でアプリケーションを開発しようとしています。ここで私はこの断片から並列データ処理で情報が混乱する

public ConcurrentDictionary<string, Info> GetDatabaseForCurrentDay(System.DateTime day) 
    { 
     string[] date = day.ToShortDateString().Split('.'); 
     string sqlQuery = "SELECT * FROM testtable WHERE Date ='" + date[2] + "-" + date[1] + "-" + date[0] + "';"; 
     ConcurrentDictionary<string, Info> info = new ConcurrentDictionary<string, Info>(); 
     Info[] dayInfo = null; 
     Parallel.ForEach(ReadData(ConnectionString, sqlQuery), data => 
     { 
      int num = 2; 
      string[] dataPieces = data.Split(new char[] { ',' }, num); 
      FileHelpers.FileHelperEngine<Info> engine = new FileHelpers.FileHelperEngine<Info>(); 
      dayInfo = engine.ReadString(dataPieces[1], int.MaxValue); 
      info.TryAdd(dataPieces[0], dayInfo[0]); 
     });  
     return info; 
    } 

別に問題に遭遇したコードの一部であり、それはループParallel.ForEachの引数を提供するので、機能ReadData(ConnectionString, sqlQuery)は、また、上述されている価値があります。

public IEnumerable<string> ReadData(string connectionString, string queryString) 
    { 
     using (MySqlConnection conn = new MySqlConnection(connectionString)) 
     { 
      using (MySqlCommand comm = new MySqlCommand(queryString, conn)) 
      { 
       conn.Open(); 
       string command2 = "USE testdatabase;"; 
       MySqlCommand commandUse = new MySqlCommand(command2, conn); 
       commandUse.ExecuteNonQuery(); 
       comm.CommandTimeout = 0; 
       MySqlDataReader reader = comm.ExecuteReader(); 
       if (reader.HasRows) 
       { 
        while (reader.Read()) 
        { 
         StringBuilder sb = new StringBuilder(); 
         sb.Append(reader.GetString(0) + ","); 
         sb.Append(reader.GetDateTime(1).ToString("yyyy-MM-dd") + ","); 
         sb.Append(reader.GetDouble(2).ToString().Replace(',', '.') + ","); 
         sb.Append(reader.GetDouble(3).ToString().Replace(',', '.') + ","); 
         sb.Append(reader.GetDouble(4).ToString().Replace(',', '.') + ","); 
         sb.Append(reader.GetDouble(5).ToString().Replace(',', '.') + ","); 
         sb.Append(reader.GetUInt64(6) + ","); 
         sb.Append(reader.GetDouble(7).ToString().Replace(',', '.')); 
         yield return sb.ToString(); 
        } 
       } 
      } 
     } 
    } 

ここで、問題に戻りましょう。コードはコンパイルされ、動作しますが、結果は正しくありません。私はConcurrentDictionaryに間違った値のキーが含まれていることに気付きました。つまり、info.TryAdd(dataPieces[0], dayInfo[0])はあるスレッドのキーと別のスレッドの値を挿入する可能性があり、データが破損する可能性があります。私はこの動作が並列処理の後退であることを理解していますが、この方法は省略できません。私はこの問題を解決するためにさまざまな方法を試しましたが、何も問題なく、データはまだ間違っていました。このコードの実行速度を維持してデータを保存するこの問題に対する解決策はありますか?

答えて

3

dayInfoをあなたの並列forループに移動する必要があります。基本的には、これは共有変数であり、それぞれのタスクによってガベージ結果が得られます。あなたがデリゲートにそれを置く場合、それは反復ごとに異なるプライベート変数となり、上書きされませ:

// Info[] dayInfo = null; <--Remove this 
Parallel.ForEach(ReadData(ConnectionString, sqlQuery), data => 
{ 
    int num = 2; 
    string[] dataPieces = data.Split(new char[] { ',' }, num); 
    FileHelpers.FileHelperEngine<Info> engine = new FileHelpers.FileHelperEngine<Info>(); 

    //declare dayInfo locally within this scope instead 
    var dayInfo = engine.ReadString(dataPieces[1], int.MaxValue); 
    info.TryAdd(dataPieces[0], dayInfo[0]); 
});  
+0

だけ明確にする:どのように正確にループ範囲からそのデータを取得するには?デリゲートを使用する場合、同じ問題の別のコピーではありませんか? – cool

+0

'dataPieces'で行ったように、既存のデリゲート内で' dayInfo'を宣言して、そのスコープにローカルにする必要があります。 – Tim

+0

修正コードのスニペットで回答を更新します。また、私は 'dayInfo'への参照が毎回新しい配列で上書きされ続けることを明確にしたかったのです。これは' Info [] 'の同じインスタンス内の値ではありません。私が記述したようにコードを変更しても、Info []のインスタンスが多少生成されるわけではなく、新しい 'Info []'変数への参照が分かれています。 – Tim