2012-05-22 2 views
1

Я пытаюсь найти способ ускорить объединение огромного количества объектов, содержащихся в списках, самым быстрым способом. Надеясь воспользоваться PLINQ, я попытался это сделать, но это не потокобезопасное решение. Я тестировал VS2010 и VS11Beta в 4.0 и 4.5. Это мое примерное приложение. Если вы измените BlowUp() между 1-500, это, как правило, будет работать. После 500 колес выходят на трассу. Это не удастся в нескольких местах. Кто-нибудь знает самый быстрый способ решить эту проблему? (Многомерный массив + PLINQ?)PLINQ ForAll взломан в .NET 4.0 и 4.5

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace PLinqBlowsUp 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     BlowUp(5000); 
    } 

    private static void BlowUp(int blowupNum) 
    { 
     try 
     { 
      var theExistingMasterListOfAllRowsOfData = new List<List<KeyValuePair<string, dynamic>>>(); 

      //Add some test data 
      Enumerable.Range(0, blowupNum).AsParallel().ForAll(row => theExistingMasterListOfAllRowsOfData.Add(AddRowToMasterList(row))); 


      var aNewRowOfData = new List<KeyValuePair<string, dynamic>>(); 
      //Add some test data 
      var column = new KeyValuePair<string, dynamic>("Title", "MyTitle"); 
      aNewRowOfData.Add(column); 

      var anotherNewRowOfData = new List<KeyValuePair<string, dynamic>>(); 
      //Add some test data 
      var columnA = new KeyValuePair<string, dynamic>("Date", DateTime.Now); 
      var columnB = new KeyValuePair<string, dynamic>("ImportantColumn", "ImportantData"); 
      var columnC = new KeyValuePair<string, dynamic>("VeryImportantColumn", "VeryImportantData"); 
      anotherNewRowOfData.Add(columnA); 
      anotherNewRowOfData.Add(columnB); 
      anotherNewRowOfData.Add(columnC); 

      //Now the Problem 
      aNewRowOfData.AsParallel().ForAll(anrod => theExistingMasterListOfAllRowsOfData.ForEach(temloarod => temloarod.Add(anrod))); 
      anotherNewRowOfData.AsParallel().ForAll(anrod => theExistingMasterListOfAllRowsOfData.ForEach(temloarod => temloarod.Add(anrod))); 

      //Test for number 
      foreach (var masterRow in theExistingMasterListOfAllRowsOfData) 
      { 
       if (masterRow.Count != 7) 
        throw new Exception("BLOW UP!!!"); 
      } 
     } 
     catch (AggregateException ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 
    } 

    private static List<KeyValuePair<string, dynamic>> AddRowToMasterList(int row) 
    { 
     var columnA = new KeyValuePair<string, dynamic>("FirstName", "John" + row.ToString()); 
     var columnB = new KeyValuePair<string, dynamic>("LastName", "Smith" + row.ToString()); 
     var columnC = new KeyValuePair<string, dynamic>("Ssn", 123456789 + (row*10)); 

     var list = new List<KeyValuePair<string, dynamic>>(); 
     list.Add(columnA); 
     list.Add(columnB); 
     list.Add(columnC); 
     return list; 
    } 
} 
} 
+0

Принимая приведенные ниже предложения ... в настоящее время пытается использовать ForAll с многомерным массивом KVP, чтобы обеспечить скорость нескольких ядер для этого типа процесса. – YurikoEX

+1

У кого есть такой вопрос? В нем говорится, что «PLA-файл ForAll не работает». Это просто неверно, и он показывает только недоразумение от вопрошающего (что является законным, никто не знает все, но в его текущей форме вопрос плох). Какая вероятность того, что ForAll будет разбита, и никто не догадывался об этом до сих пор? – sloth

ответ

3

Я вижу две проблемы.

  • Вы звоните Add на экземпляре theExistingMasterListOfAllRowsOfData из более чем одной нити без каких-либо попыток синхронизации доступа к нему.
  • Вы вызываете Add на отдельных List<KeyValuePair<string, dynamic>> элементах из нескольких потоков без каких-либо попыток их синхронизации.

Вы можете использовать lock для защиты Add методы или использовать ConcurrentBag вместо этого. Однако ни один из этих вариантов не является хорошим. Проблема здесь в том, что этот вид операции нельзя распараллелить очень хорошо, потому что все потоки будут конкурировать за одну и ту же блокировку. Я очень подозреваю, что даже низкий уровень блокировки ConcurrentBag будет медленнее, чем если бы вы просто набросились на PLINQ и сделали все на основной нити, чтобы начать.

2

PLINQ не является заменой для написания поточно-кода. Ваш доступ к theExistingMasterListOfAllRowsOfData не является потокобезопасным, поскольку к нему обращаются все потоки из пула потоков. Вы можете попробовать блокировки вокруг него, что решить эту проблему для меня:

Enumerable.Range(0, blowupNum).AsParallel().ForAll(row => { 
    lock (theExistingMasterListOfAllRowsOfData) {     
     theExistingMasterListOfAllRowsOfData.Add(AddRowToMasterList(row)); 
    } 
}); 

Однако, блокировка не может быть то, что вы после этого, так как это будет ввести узкие места.

+0

Я думал о блокировке, но в первую очередь убивал точку ForAll(). – YurikoEX

+1

Несомненно, но дело в том, что PLink - это просто другой способ выделения и использования нескольких потоков. Вы должны знать обо всех проблемах и проблемах написания многопоточного кода, создаете ли вы свои собственные потоки, вручную вытягиваете потоки из пула потоков или используете PLink. –

4

Это не имеет никакого отношения к PLinq - добавление элемента в List<T> просто не является потокобезопасным. Работоспособное решение, которое ухудшало бы производительность, должно было бы ввести блокировку. Вместо этого вы планируете создать новую коллекцию в результате вашего заявления PLinq - введение побочных эффектов, как и вы, скорее не в духе Linq/функционального программирования в целом, и вы можете столкнуться с трудностями (как и вы).

+0

Кажется, что для ForAll было больше. Если бы я не хотел влиять на мои списки, я бы не стал ForEach/ForAll'ing их. Просто делает ForAll кажутся бессмысленными для списков (так как я должен придумать способ ограничить его блокировкой). Но делать .ForEach, чтобы повлиять на мою коллекцию, полностью в духе Linq. Так почему же нет. ForAll? – YurikoEX

Смежные вопросы