我可以使用parallel.ForEach调用不同的函数吗?

问题描述:

我有一个运行的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 2012-04-17 15:24:40

当然,这是完全可能的。 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 2012-04-17 15:25:05

+0

用计数器代码更新;我的坏,我没有看到聚合。 – Tejs 2012-04-17 15:28:31

你可以尝试这样的事情,更实用的风格:

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

它是喜欢首先(内部)选择列表中的所有记录,然后总和功能? – Pankaj 2012-04-17 15:45:48

+1

是的 - 但选择被称为“它走了” - 所以它可能会具有相同的性能配置文件,从上面的六个字母变量 – 2012-04-17 16:21:21

你可以使用AsParallelSum

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 2012-04-17 15:48:34

+0

@PankajGarg:我不知道,在实践中可能没有区别。 – user7116 2012-04-17 16:15:29

+0

在幕后,这可能完全一样。 – 2012-04-17 16:20:04

的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); 
    }); 

这是如何工作的。第一个lambda表达式初始化TPL释放的每个工人的本地小计仓。第二个lambda表达式对集合中的每个项目执行一次,并更新本地小计仓。第三个lambda表达式将本地小计箱组合到最终总计中。

使用Parallel.ForEachAsParallel与合计之间的一个有趣的区别是小计累计到最终值的方式。 Parallel.ForEach在工作线程上执行此操作,因此需要线程安全操作Interlocked.AddAsParallel使用相同的分区策略,但会在调用者线程上累积小计。