2012-04-17 3 views
4

У меня есть цикл foreach, который работает. Я рассматриваю Параллельные функции. Можно ли преобразовать следующий код для использования параллельного программирования?Могу ли я использовать parallel.ForEach для вызова различных функций?

int result ; 
int counter; 
foreach(DataRow dr in ds.Tables[0].Rows) { 
    switch(dr["Gender"].ToString()) { 
     case "Male": 
      result = functionMale(dr["Gender"].ToString()); 
      counter += result; 
      break; 
     case "Female": 
      result = functionFemale(dr["Gender"].ToString()); 
      counter += result; 
      break; 
     default: 
      result = functionUnkown(dr["Gender"].ToString()); 
      counter += result; 
      break; 
    } 
} 

Основываясь на том, что я изучил, у меня есть только следующее.

Parallel.ForEach(ds.Tables[0].AsEnumerable(), dr => { 
    var result = functionMale(dr["Gender"].ToString(); 
}); 

Любые идеи? Спасибо

+0

Вот ссылка на агрегирование в параллельной среде на C# http://msdn.microsoft.com/en-us/library/ff963547.aspx – Servy

ответ

0

Несомненно, это вполне возможно. Parallel.ForEach разве делать какой-либо конкретной магии в этом случае (кроме резьбы), так что это будет просто выглядеть следующим образом:

ds.Tables[0].Rows.AsEnumerable().AsParallel().Sum(x => 
    { 
     DataRow dr = x as DataRow; 

     switch(dr["Gender"].ToString()) 
     { 
      case "Male": 
      // Stuff 
      case "Female"; 
      // Stuff 
      default: 
      // Stuff 
     } 

     return counter; 
    }); 

Это должно объединить все значения функций, так как сложение коммутативно.

+1

У них такая зависимость; он агрегирует данные. – Servy

+0

Обновлено кодом счетчика; мой плохой, я не видел агрегацию. – Tejs

3

Вы могли бы попробовать что-то вроде этого, в более функциональном стиле:

var counter = 
    ds.Tables[0].AsEnumerable() 
    .AsParallel() 
    .Select(dr => { 
     var gender = dr["Gender"].ToString(); 

     switch(gender) 
     { 
      case "Male": 
       return functionMale(gender); 
      case "Female": 
       return functionFemale(gender); 
      default: 
       return functionUnkown(gender); 
     } 
    }) 
    .Sum(); 
+0

Это как выбрать все записи в списке сначала (внутренне), а затем функцию sum? – Pankaj

+1

Да - но выбор называется «как он идет» - так что, вероятно, он будет иметь тот же профиль производительности, что и другой ответ выше из шестизначных переменных. –

5

Вы можете использовать AsParallel и Sum:

Func<string, int> calculateGender = 
    gender => 
    { 
     // nb: I don't know why you pass the gender to the method, but 
     // I've left your intent as-is 
     switch (gender) 
     { 
      case "Male": return functionMale(gender); 
      case "Female": return functionFemale(gender); 
      default:  return functionUnknown(gender); 
     } 
    }; 

int counter = ds.Tables[0].AsEnumerable() 
          .AsParallel() 
          .Sum(dr => calculateGender(dr["Gender"].ToString())); 
+0

Я немного смущен. @Dave делает то же самое. так что быстрее? – Pankaj

+0

@PankajGarg: Я не знаю, скорее всего, никакой разницы на практике. – user7116

+0

Это, наверное, точно так же, за кулисами. –

0

PLINQ подход намного проще, но просто округлить вот ответы, вот как вы это сделаете с Parallel.ForEach.

int counter = 0; 
Parallel.ForEach(ds.Tables[0].AsEnumerable(), 
() => /* subtotal initializer */ 
    { 
    return 0; 
    }, 
    (dr, state, subtotal) => /* loop body */ 
    { 
    switch(dr["Gender"].ToString()) 
    { 
     case "Male": 
      subtotal += functionMale(dr["Gender"].ToString()); 
      break; 
     case "Female": 
      subtotal += functionFemale(dr["Gender"].ToString()); 
      break; 
     default: 
      subtotal += functionUnkown(dr["Gender"].ToString()); 
      break; 
    } 
    }, 
    subtotal => /* subtotal accumulator */ 
    { 
    Interlocked.Add(ref counter, subtotal); 
    }); 

Вот как это работает. Первое выражение лямбда инициализирует локальные промежуточные бункеры для каждого рабочего, из которых TPL. Второе лямбда-выражение выполняется один раз для каждого элемента коллекции и обновляет локальный промежуточный бит. Третье лямбда-выражение объединяет локальные промежуточные бункеры в итоговую сумму.

Одно интересное различие между использованиеми AsParallel с агрегатом заключается в том, как промежуточные суммы накапливаются в конечном значении. Parallel.ForEach выполняет эту операцию на рабочей нити, таким образом, необходимость в потокобезопасной операции Interlocked.Add. AsParallel использует ту же стратегию парирования, но накапливает промежуточные итоги по потоку вызывающих.

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